ฉันจะจัดโครงสร้างระบบโหลดสินทรัพย์ที่ขยายได้อย่างไร


19

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

โดยเฉพาะฉันกำลังมองหาวิธีที่ฉันสามารถออกแบบผู้จัดการในลักษณะเพื่อให้เป็นนามธรรมว่ามีการโหลดประเภทสินทรัพย์ที่เฉพาะเจาะจงและการโหลดเนื้อหาที่ไหน ฉันต้องการให้สามารถรองรับทั้งระบบไฟล์และที่เก็บ RDBMS โดยไม่ต้องใช้โปรแกรมอื่น ๆ ที่จำเป็นต้องรู้ ในทำนองเดียวกันฉันต้องการเพิ่มเนื้อหาคำอธิบายภาพเคลื่อนไหว (FPS เฟรมที่จะแสดงภาพอ้างอิงภาพสไปรต์และอื่น ๆ ) นั่นคือ XML ฉันควรจะสามารถเขียนคลาสสำหรับสิ่งนี้ด้วยฟังก์ชันการทำงานเพื่อค้นหาและอ่านไฟล์ XML และสร้างและส่งคืนAnimationAssetคลาสด้วยข้อมูลนั้น ฉันกำลังมองหาการออกแบบที่ขับเคลื่อนด้วยข้อมูล

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

คำตอบ:


23

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

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

เพื่อจัดการกับข้อกังวลเฉพาะของคุณคุณควรออกแบบตัวโหลดของคุณเพื่อไม่ให้โหลดเนื้อหาใด ๆ แต่ให้มอบหมายความรับผิดชอบให้กับอินเทอร์เฟซที่ปรับให้เหมาะกับการโหลดเนื้อหาประเภทใดประเภทหนึ่งโดยเฉพาะ ตัวอย่างเช่น:

interface ITypeLoader {
  object Load (Stream assetStream);
}

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

ตัวโหลดสินทรัพย์หลักของคุณจะต้องสามารถลงทะเบียนและติดตามตัวโหลดประเภทเฉพาะเหล่านี้:

class AssetLoader {
  public void RegisterType (string key, ITypeLoader loader) {
    loaders[key] = loader;
  }

  Dictionary<string, ITypeLoader> loaders = new Dictionary<string, ITypeLoader>();
}

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

ผู้ใช้ควรอ้างถึงเนื้อหาที่มีข้อมูลขั้นต่ำเปล่า ในบางกรณีชื่อไฟล์เพียงอย่างเดียวก็เพียงพอแล้ว แต่ฉันพบว่าบ่อยครั้งที่ต้องการใช้คู่ชนิด / ชื่อเพื่อให้ทุกอย่างชัดเจนมาก ดังนั้นผู้ใช้อาจอ้างถึงอินสแตนซ์ที่มีชื่อของไฟล์ XML ภาพเคลื่อนไหวหนึ่งไฟล์ของ"AnimationXml","PlayerWalkCycle"คุณ

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

interface IAssetStreamProvider {
  Stream GetStream (string type, string name);
}

class AssetLoader {
  public AssetLoader (IAssetStreamProvider streamProvider) {
    provider = streamProvider;
  }

  object LoadAsset (string type, string name) {
    var loader = loaders[type];
    var stream = provider.GetStream(type, name);

    return loader.Load(stream);
  }

  public void RegisterType (string type, ITypeLoader loader) {
    loaders[type] = loader;
  }

  IAssetStreamProvider provider;
  Dictionary<string, ITypeLoader> loaders = new Dictionary<string, ITypeLoader>();
}

ผู้ให้บริการสตรีมแบบง่าย ๆ เพียงแค่ค้นหาในไดเรกทอรีรากของสินทรัพย์ที่ระบุสำหรับไดเรกทอรีย่อยที่มีชื่อtypeและโหลดไบต์ดิบของไฟล์ชื่อnameลงในสตรีมแล้วส่งคืน

ในระยะสั้นสิ่งที่คุณมีที่นี่คือระบบที่:

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

คำเตือนและหมายเหตุสุดท้ายบางประการ:

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

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

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

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


1
คำถามที่ดีและคำตอบที่ดีที่ขับเคลื่อนโซลูชันไม่ใช่เพียงแค่การออกแบบที่ขับเคลื่อนด้วยข้อมูล แต่ยังรวมถึงวิธีการเริ่มคิดด้วยวิธีที่ขับเคลื่อนด้วยข้อมูล
Patrick Hughes

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

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

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

วิธีการนี้ได้รับเกียรตินิยมบางส่วนของหลักการของทั้งของแข็งและOOP ไชโย
อดัมเนย์เลอร์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.