วิธีหลีกเลี่ยง“ ผู้จัดการ” ในรหัสของฉัน


26

ขณะนี้ฉันกำลังออกแบบระบบ Entityของฉันใหม่สำหรับ C ++ และฉันมีผู้จัดการจำนวนมาก ในการออกแบบของฉันฉันมีชั้นเรียนเหล่านี้เพื่อผูกห้องสมุดของฉันเข้าด้วยกัน ฉันได้ยินสิ่งเลวร้ายมากมายเมื่อพูดถึงคลาส "ผู้จัดการ" บางทีฉันอาจไม่ได้ตั้งชื่อชั้นเรียนของฉันอย่างเหมาะสม อย่างไรก็ตามฉันไม่รู้ว่าจะตั้งชื่อพวกเขาอย่างไร

ผู้จัดการส่วนใหญ่ในห้องสมุดของฉันประกอบด้วยชั้นเรียนเหล่านี้ (แม้ว่ามันจะแตกต่างกันเล็กน้อย):

  • คอนเทนเนอร์ - คอนเทนเนอร์สำหรับวัตถุในตัวจัดการ
  • คุณสมบัติ - คุณลักษณะสำหรับวัตถุในผู้จัดการ

ในการออกแบบใหม่สำหรับห้องสมุดของฉันฉันมีชั้นเรียนเฉพาะเหล่านี้เพื่อผูกห้องสมุดของฉันเข้าด้วยกัน

  • ComponentManager - จัดการส่วนประกอบใน Entity System

    • ComponentContainer
    • ComponentAttributes
    • ฉาก * - อ้างอิงถึงฉาก (ดูด้านล่าง)
  • SystemManager - จัดการระบบในระบบองค์กร

    • SystemContainer
    • ฉาก * - อ้างอิงถึงฉาก (ดูด้านล่าง)
  • EntityManager - จัดการเอนทิตีใน Entity System

    • EntityPool - กลุ่มของเอนทิตี
    • EntityAttributes - คุณสมบัติของเอนทิตี (ซึ่งจะสามารถเข้าถึงคลาส ComponentContainer และ System เท่านั้น)
    • ฉาก * - อ้างอิงถึงฉาก (ดูด้านล่าง)
  • ฉาก - เชื่อมโยงผู้จัดการทั้งหมดเข้าด้วยกัน

    • ComponentManager
    • SystemManager
    • EntityManager

ฉันกำลังคิดว่าจะแค่วางคอนเทนเนอร์ / พูลทั้งหมดในคลาสของตัวเอง

กล่าวคือ

แทนสิ่งนี้:

Scene scene; // create a Scene

// NOTE:
// I technically could wrap this line in a createEntity() call in the Scene class
Entity entity = scene.getEntityManager().getPool().create();

มันจะเป็นแบบนี้:

Scene scene; // create a Scene

Entity entity = scene.getEntityPool().create();

แต่ฉันไม่แน่ใจ ถ้าฉันจะทำสิ่งหลังสุดนั่นก็หมายความว่าฉันจะมีวัตถุและวิธีการมากมายในชั้นเรียนของฉัน

หมายเหตุ:

  1. ระบบเอนทิตีเป็นเพียงการออกแบบที่ใช้สำหรับเกม ประกอบด้วย 3 ส่วนหลักคือส่วนประกอบเอนทิตีและระบบ ส่วนประกอบเป็นเพียงข้อมูลซึ่งอาจ "เพิ่ม" ให้กับเอนทิตีเพื่อให้เอนทิตีมีความโดดเด่น เอนทิตีถูกแทนด้วยจำนวนเต็ม ระบบประกอบด้วยตรรกะสำหรับองค์กรที่มีส่วนประกอบเฉพาะ
  2. เหตุผลที่ฉันเปลี่ยนการออกแบบสำหรับห้องสมุดของฉันเป็นเพราะฉันคิดว่ามันสามารถเปลี่ยนแปลงได้มากฉันไม่ชอบความรู้สึก / ไหลไปในขณะนี้

คุณช่วยอธิบายรายละเอียดเกี่ยวกับสิ่งเลวร้ายที่คุณได้ยินเกี่ยวกับผู้จัดการได้อย่างไรและพวกเขาเกี่ยวข้องกับคุณอย่างไร
MrFox


@MrFox โดยทั่วไปฉันได้ยินสิ่งที่ Dunk ได้พูดถึงและฉันมีคลาส * Manager จำนวนมากในห้องสมุดของฉันซึ่งฉันต้องการกำจัด
miguel.martin

สิ่งนี้อาจมีประโยชน์เช่นกัน: medium.com/@wrong.about/…
Zapadlo

คุณแยกเฟรมเวิร์กที่มีประโยชน์ออกจากแอปพลิเคชันที่ใช้งานได้ แทบเป็นไปไม่ได้ที่จะเขียนกรอบงานที่มีประโยชน์สำหรับแอปพลิเคชันที่จินตนาการ
วินไคลน์

คำตอบ:


19

DeadMG เป็นจุดเฉพาะของรหัสของคุณ แต่ฉันรู้สึกว่ามันไม่ชัดเจน นอกจากนี้ฉันไม่เห็นด้วยกับคำแนะนำของเขาที่ไม่ได้อยู่ในบริบทเฉพาะเช่นการพัฒนาวิดีโอเกมที่มีประสิทธิภาพสูงสุดเช่น แต่เขาอยู่ทั่วโลกว่าส่วนใหญ่ของรหัสของคุณไม่ได้มีประโยชน์ในขณะนี้

ดังที่ Dunk กล่าวว่าคลาสผู้จัดการถูกเรียกเช่นนี้เพราะพวกเขา "จัดการ" สิ่งต่าง ๆ "จัดการ" ก็เหมือนกับ "ข้อมูล" หรือ "ทำ" มันเป็นคำที่เป็นนามธรรมที่สามารถมีเกือบทุกอย่าง

ฉันส่วนใหญ่อยู่ในสถานที่เดียวกันกับคุณเมื่อประมาณ 7 ปีที่แล้วและฉันเริ่มคิดว่ามีบางอย่างผิดปกติในความคิดของฉันเพราะมันเป็นความพยายามอย่างมากที่จะเขียนโค้ดเพื่อไม่ทำอะไรเลย

สิ่งที่ฉันเปลี่ยนเพื่อแก้ไขนี่คือการเปลี่ยนคำศัพท์ที่ฉันใช้ในรหัส ฉันหลีกเลี่ยงคำทั่วไปโดยสิ้นเชิง (ยกเว้นว่าเป็นรหัสทั่วไป แต่ก็ไม่ค่อยเกิดขึ้นเมื่อคุณไม่ได้สร้างห้องสมุดทั่วไป) ฉันหลีกเลี่ยงการตั้งชื่อ "ผู้จัดการ"หรือ "วัตถุ" ประเภทใดก็ได้

ผลกระทบนั้นเป็นรหัสโดยตรง: มันบังคับให้คุณค้นหาคำที่ตรงกับความรับผิดชอบที่แท้จริงของประเภทของคุณ หากคุณรู้สึกว่าประเภททำหลายสิ่ง (เก็บดัชนีของหนังสือรักษาพวกมันให้มีชีวิตสร้าง / ทำลายหนังสือ) จากนั้นคุณต้องมีประเภทที่แตกต่างกันซึ่งแต่ละคนจะมีความรับผิดชอบเพียงอย่างเดียว บางครั้งฉันต้องการโรงงาน แต่ก็ไม่ใช่ บางครั้งฉันต้องการรีจิสตรีดังนั้นฉันจึงเซ็ตอัพหนึ่งครั้ง (โดยใช้คอนเทนเนอร์มาตรฐานและตัวชี้อัจฉริยะ) บางครั้งฉันต้องการระบบที่ประกอบด้วยระบบย่อยที่แตกต่างกันหลายอย่างดังนั้นฉันจึงแยกทุกอย่างเท่าที่จะทำได้เพื่อให้แต่ละส่วนทำสิ่งที่มีประโยชน์

อย่าตั้งชื่อ "ผู้จัดการ" ประเภทและตรวจสอบให้แน่ใจว่าประเภทของคุณทั้งหมดมีบทบาทที่ไม่ซ้ำกันคือคำแนะนำของฉัน อาจเป็นเรื่องยากที่จะหาชื่อบางครั้ง แต่มันเป็นหนึ่งในสิ่งที่ยากที่สุดในการเขียนโปรแกรมโดยทั่วไป


หากคู่dynamic_castของคุณกำลังฆ่าประสิทธิภาพของคุณให้ใช้ระบบประเภทสแตติก - นั่นคือสิ่งที่มันต้องการ มันเป็นบริบทที่เฉพาะเจาะจงไม่ใช่บริบททั่วไปที่จะต้องพลิก RTTI ของคุณเองอย่าง LLVM ถึงอย่างนั้นพวกเขาก็ไม่ทำเช่นนี้
DeadMG

@DeadMG ฉันไม่ได้พูดถึงส่วนนี้โดยเฉพาะ แต่เกมที่มีประสิทธิภาพสูงส่วนใหญ่ไม่สามารถจ่ายได้เช่นการจัดสรรแบบไดนามิกผ่านสิ่งใหม่หรือแม้กระทั่งสิ่งอื่น ๆ ที่ดีสำหรับการเขียนโปรแกรมทั่วไป พวกมันเป็นสัตว์ร้ายที่เฉพาะเจาะจงสำหรับฮาร์ดแวร์เฉพาะที่คุณไม่สามารถพูดคุยได้ทั่วไปซึ่งเป็นสาเหตุที่ "มันขึ้นอยู่กับ" เป็นคำตอบที่กว้างกว่าโดยเฉพาะกับ C ++ (ไม่มีใครในเกม dev ที่ใช้การร่ายแบบไดนามิกไม่เคยมีประโยชน์จนกว่าคุณจะมีปลั๊กอินและแม้กระทั่งมันก็หายาก)
Klaim

@DeadMG ฉันไม่ได้ใช้ dynamic_cast และฉันไม่ได้ใช้ทางเลือกในการ dynamic_cast ฉันนำมันไปใช้ในห้องสมุด แต่ฉันไม่สามารถแยกมันออกได้ template <typename T> class Class { ... };เป็นจริงสำหรับการกำหนด ID สำหรับคลาสที่แตกต่างกัน (เช่นคอมโพเนนต์ที่กำหนดเองและระบบที่กำหนดเอง) การกำหนด ID ใช้เพื่อจัดเก็บส่วนประกอบ / ระบบในเวกเตอร์เนื่องจากแผนที่อาจไม่ได้ประสิทธิภาพที่ฉันต้องการสำหรับเกม
miguel.martin

ดีฉันไม่ได้ใช้ทางเลือกในการ dynamic_cast อย่างใดอย่างหนึ่งเพียงแค่ type_id ปลอม แต่ถึงกระนั้นฉันก็ไม่ต้องการสิ่งที่ type_id ประสบความสำเร็จ
miguel.martin

"ค้นหาคำที่ถูกต้องซึ่งสอดคล้องกับความรับผิดชอบที่แท้จริงของประเภทของคุณ" - แม้ว่าฉันจะเห็นด้วยกับสิ่งนี้ทั้งหมด แต่บ่อยครั้งที่ไม่มีคำเดียว (ถ้ามี) ที่ชุมชนนักพัฒนาได้ตกลงกันสำหรับความรับผิดชอบเฉพาะประเภท
bytedev

23

ฉันได้อ่านโค้ดที่คุณเชื่อมโยงและโพสต์ของคุณและบทสรุปที่ซื่อสัตย์ของฉันก็คือว่าส่วนใหญ่ไม่มีประโยชน์โดยสิ้นเชิง ขอโทษ ฉันหมายความว่าคุณมีรหัสทั้งหมดนี้ แต่คุณไม่ได้ทำอะไรเลย เลย ฉันจะต้องไปที่ลึกลงไปที่นี่ดังนั้นทนกับฉัน

เริ่มต้น Let 's กับObjectFactory ประการแรกตัวชี้ฟังก์ชั่น สิ่งเหล่านี้คือไร้สัญชาติวิธีเดียวที่ไร้ประโยชน์ที่มีประโยชน์ในการสร้างวัตถุคือnewและคุณไม่จำเป็นต้องมีโรงงานสำหรับสิ่งนั้น การสร้างออบเจ็กต์อย่างเป็นมลรัฐคือสิ่งที่มีประโยชน์และพอยน์เตอร์ของฟังก์ชั่นไม่เป็นประโยชน์สำหรับงานนี้ และประการที่สองวัตถุทุกชิ้นจำเป็นต้องรู้วิธีทำลายตัวเองโดยไม่ต้องค้นหาตัวอย่างการสร้างที่ถูกต้องเพื่อทำลายมัน และนอกจากนี้คุณต้องส่งคืนตัวชี้สมาร์ทไม่ใช่ตัวชี้แบบดิบสำหรับข้อยกเว้นและความปลอดภัยทรัพยากรของผู้ใช้ของคุณ คลาส ClassRegistryData ทั้งหมดของคุณเป็นเพียงstd::function<std::unique_ptr<Base, std::function<void(Base*)>>()>แต่มีหลุมดังกล่าว มันไม่จำเป็นต้องมีอยู่เลย

จากนั้นเรามี AllocatorFunctor- มีอินเทอร์เฟซตัวจัดสรรมาตรฐานที่ให้ไว้ - ไม่ต้องพูดถึงว่าพวกเขาเพิ่งวางบนdelete obj;และreturn new T;- ซึ่งมีให้อีกครั้งแล้วและพวกเขาจะไม่โต้ตอบกับตัวจัดสรรหน่วยความจำที่กำหนดเอง

และ ClassRegistry นั้นเรียบง่ายstd::map<std::string, std::function<std::unique_ptr<Base, std::function<void(Base*)>>()>>แต่คุณเขียนคลาสสำหรับมันแทนที่จะใช้ฟังก์ชันที่มีอยู่ มันเหมือนกัน แต่ไม่แน่นอนทั่วโลกที่ไม่แน่นอนอย่างสมบูรณ์ด้วยมาโครแบบเส็งเคร็ง

สิ่งที่ฉันพูดที่นี่คือคุณได้สร้างคลาสที่คุณสามารถแทนที่สิ่งทั้งหมดด้วยฟังก์ชันที่สร้างขึ้นจากคลาสมาตรฐานและทำให้แย่ลงกว่าเดิมด้วยการทำให้พวกเขาอยู่ในสภาพที่ไม่แน่นอนในระดับโลกและเกี่ยวข้องกับมาโครจำนวนมาก สิ่งที่เลวร้ายที่สุดที่คุณสามารถทำได้

แล้วเรามีระบุตัวตนได้ มันเป็นเรื่องเดียวกันมากที่นี่std::type_infoแล้วทำหน้าที่ระบุแต่ละประเภทว่าเป็นค่ารันไทม์และไม่จำเป็นต้องใช้แผ่นสำเร็จรูปมากเกินไปในส่วนของผู้ใช้

    template<typename T, typename Y>
    bool isSameType(const X& x, const Y& y) { return typeid(x) == typeid(y); }
    template <class Type, class T>
    bool isType(const T& obj)
    {
            return dynamic_cast<const Type*>(std::addressof(obj));
    }

แก้ไขปัญหาแล้ว แต่ไม่มีมาโครสองร้อยบรรทัดมาก่อนและอะไรก็ตาม สิ่งนี้ยังทำเครื่องหมายIdentifiableArrayของคุณว่าไม่มีอะไร แต่std::vectorคุณต้องใช้การนับการอ้างอิงถึงแม้ว่าการเป็นเจ้าของที่ไม่ซ้ำกันหรือไม่ใช่เจ้าของจะดีกว่า

โอ้และฉันไม่ได้พูดถึงประเภทที่มีกลุ่มของtypedefs ไร้ประโยชน์อย่างแน่นอน ? คุณจะไม่ได้รับประโยชน์ใด ๆ จากการใช้ประเภทดิบโดยตรงและไม่สามารถอ่านได้

ขึ้นต่อไปคือComponentContainer คุณไม่สามารถเลือกความเป็นเจ้าของได้คุณไม่สามารถแยกคอนเทนเนอร์สำหรับคลาสที่ได้รับของส่วนประกอบต่าง ๆ ได้คุณไม่สามารถใช้ซีแมนทิกส์อายุการใช้งานของคุณเอง ลบโดยไม่สำนึกผิด

ตอนนี้ComponentFilterอาจมีค่าเพียงเล็กน้อยถ้าคุณปรับโครงสร้างใหม่เป็นจำนวนมาก สิ่งที่ต้องการ

class ComponentFilter {
    std::unordered_map<std::type_info, unsigned> types;
public:
    template<typename T> void SetTypeRequirement(unsigned count = 1) {
        types[typeid(T)] = count;
    }
    void clear() { types.clear(); }
    template<typename Iterator> bool matches(Iterator begin, Iterator end) {
        std::for_each(begin, end, [this](const std::iterator_traits<Iterator>::value_type& t) {
            types[typeid(t)]--;
        });
        return types.empty();
    }
};

สิ่งนี้ไม่ได้จัดการกับคลาสที่ได้มาจากสิ่งที่คุณพยายามจะจัดการ

เรื่องที่เหลือเป็นเรื่องเดียวกัน ไม่มีอะไรที่นี่ที่ไม่สามารถทำได้ดีกว่านี้มากหากไม่ใช้ห้องสมุดของคุณ

คุณจะหลีกเลี่ยงผู้จัดการในรหัสนี้ได้อย่างไร ลบทิ้งทั้งหมด


14
ของผมในขณะที่จะเรียนรู้ แต่ที่น่าเชื่อถือที่สุดชิ้นข้อผิดพลาดฟรีโค้ดเป็นคนที่ไม่ได้มี
Tacroy

1
เหตุผลที่ฉันไม่ได้ใช้ std :: type_info เป็นเพราะฉันพยายามหลีกเลี่ยง RTTI ที่ผมกล่าวถึงกรอบการมุ่งเป้าไปที่เกมที่เป็นพื้นเหตุผลที่ฉันไม่ได้ใช้หรือtypeid dynamic_castขอบคุณสำหรับคำติชมฉันขอขอบคุณ
miguel.martin

นักวิจารณ์คนนี้มีพื้นฐานมาจากความรู้ของคุณเกี่ยวกับเอ็นจินเกมระบบเอนทิตีหรือเป็นรีวิวโค้ด C ++ ทั่วไปหรือไม่?
ชัด

1
@ ฉันคิดว่าปัญหาการออกแบบมากกว่าสิ่งอื่นใด
miguel.martin

10

ปัญหาเกี่ยวกับคลาสของผู้จัดการคือผู้จัดการสามารถทำอะไรก็ได้ คุณไม่สามารถอ่านชื่อคลาสและรู้ว่าคลาสทำอะไร EntityManager .... ฉันรู้ว่ามันทำอะไรกับ Entities แต่ใครจะรู้อะไร SystemManager ... อ๋อมันจัดการระบบ มีประโยชน์มาก

ฉันเดาว่าชั้นเรียนผู้จัดการของคุณอาจทำสิ่งต่างๆมากมาย ฉันจะเดิมพันถ้าคุณแบ่งส่วนเหล่านั้นออกเป็นส่วนที่เกี่ยวข้องกับการทำงานอย่างใกล้ชิดคุณอาจจะสามารถสร้างชื่อคลาสที่เกี่ยวข้องกับชิ้นส่วนการทำงานที่ทุกคนสามารถบอกได้ว่าพวกเขาทำอะไรง่ายๆโดยดูที่ชื่อชั้นเรียน สิ่งนี้จะทำให้การบำรุงรักษาและเข้าใจการออกแบบง่ายขึ้น


1
+1 และไม่เพียง แต่พวกเขาสามารถทำอะไรได้ในระยะเวลาที่นานพอที่พวกเขาจะทำ ผู้คนมองว่าคำว่า "ผู้จัดการ" เป็นคำเชิญให้สร้างคลาสมีดครัว / อ่างมีดสวิสกองทัพ
Erik Dietrich

@Erik - อ่าใช่ฉันควรจะเพิ่มว่าการมีชื่อชั้นเรียนที่ดีนั้นไม่เพียง แต่มีความสำคัญเพราะมันบอกใครบางคนว่าชั้นเรียนทำอะไรได้บ้าง แต่ชื่อคลาสก็บอกว่าคลาสไม่ควรทำเช่นเดียวกัน
Dunk

3

จริง ๆ แล้วฉันไม่คิดว่าManagerจะเลวร้ายในบริบทนี้ยัก หากมีสถานที่แห่งหนึ่งที่ฉันคิดว่าManagerสามารถให้อภัยได้ก็คงเป็นเพราะระบบส่วนประกอบของเอนทิตี้เพราะมันคือ " การจัดการ " อายุการใช้งานของส่วนประกอบเอนทิตีและระบบ อย่างน้อยที่สุดก็เป็นชื่อที่มีความหมายมากกว่าที่นี่พูดFactoryซึ่งไม่สมเหตุสมผลในบริบทนี้เนื่องจาก ECS ทำสิ่งที่มากกว่าทำสิ่งต่าง ๆ อย่างแท้จริง

ฉันคิดว่าคุณสามารถใช้เช่นDatabase ComponentDatabaseหรือคุณก็อาจใช้Components, SystemsและEntities(นี่คือสิ่งที่ผมเองใช้และส่วนใหญ่เพียงเพราะฉันเหมือนตัวระบุสั้นแม้ว่ามันจะเป็นที่ยอมรับบ่งบอกถึงแม้ข้อมูลน้อยบางทีกว่า "ผู้จัดการ")

ส่วนหนึ่งที่ฉันพบเงอะงะเล็กน้อยคือ:

Entity entity = scene.getEntityManager().getPool().create();

นั่นเป็นสิ่งที่ต้องทำgetก่อนที่คุณจะสามารถสร้างเอนทิตีและรู้สึกว่าการออกแบบกำลังทำให้รายละเอียดการใช้งานรั่วไปเล็กน้อยหากคุณต้องรู้เกี่ยวกับเอนทิตีพูลและ "ผู้จัดการ" เพื่อสร้างพวกเขา ฉันคิดว่าสิ่งต่าง ๆ เช่น "พูล" และ "ผู้จัดการ" (ถ้าคุณใช้ชื่อนี้) ควรเป็นรายละเอียดการใช้งานของ ECS ไม่ใช่สิ่งที่ลูกค้าเห็น

แต่ dunno ฉันได้เห็นบางใช้โง่มากและทั่วไปManager, Handler, Adapterและชื่อของประเภทนี้ แต่ฉันคิดว่านี้เป็นกรณีที่ใช้อภัยพอสมควรเมื่อสิ่งที่เรียกว่า "ผู้จัดการ" เป็นจริงรับผิดชอบในการ "จัดการ" ( "การควบคุม? "," การจัดการ? ") อายุการใช้งานของวัตถุมีความรับผิดชอบในการสร้างและทำลายและให้การเข้าถึงพวกเขาเป็นรายบุคคล นั่นคือการใช้ "ผู้จัดการ" ที่ตรงไปตรงมาที่สุดและใช้งานง่ายสำหรับฉันอย่างน้อย

ที่กล่าวว่าหนึ่งในกลยุทธ์ทั่วไปเพื่อหลีกเลี่ยง "ผู้จัดการ" ในชื่อของคุณเป็นเพียงส่วน "ผู้จัดการ" ออกและเพิ่ม-sในตอนท้าย :-D ตัวอย่างเช่นสมมติว่าคุณมีThreadManagerและรับผิดชอบในการสร้างเธรดและให้ข้อมูลเกี่ยวกับพวกเขาและการเข้าถึงพวกเขาและอื่น ๆ แต่มันไม่ได้รวมเธรดดังนั้นเราจึงไม่สามารถเรียกมันThreadPoolได้ Threadsทั้งในกรณีที่คุณเพียงแค่เรียกมันว่า นั่นคือสิ่งที่ฉันทำอยู่แล้วเมื่อฉันไม่เข้าใจ

ฉันค่อนข้างจะนึกย้อนกลับไปเมื่อมีการเปรียบเทียบแอนะล็อกของ "Windows Explorer" เรียกว่า "ตัวจัดการไฟล์" เช่น:

ป้อนคำอธิบายรูปภาพที่นี่

และฉันก็คิดว่า "ตัวจัดการไฟล์" ในกรณีนี้มีความหมายมากกว่า "Windows Explorer" แม้ว่าจะมีชื่อที่ดีกว่าซึ่งไม่เกี่ยวข้องกับ "ตัวจัดการ" อย่างน้อยที่สุดโปรดอย่าคิดมากกับชื่อของคุณและเริ่มตั้งชื่อเช่น "ComponentExplorer" "ComponentManager" อย่างน้อยก็ทำให้ฉันมีความคิดที่สมเหตุสมผลว่าสิ่งนั้นทำอะไรได้บ้างเมื่อมีคนเคยทำงานกับเครื่องยนต์ของ ECS


ตกลง หากมีการใช้คลาสสำหรับหน้าที่การจัดการทั่วไป (สร้าง ("จ้าง") ทำลาย ("ยิง") กำกับดูแลและ "พนักงาน" / ส่วนต่อประสานที่สูงขึ้นดังนั้นชื่อManagerมีความเหมาะสมคลาสอาจมีปัญหาการออกแบบแต่ชื่อ เป็นจุดบน
Justin เวลา 2 Reinstate โมนิกา
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.