คลาสพื้นฐานเป็นโรงงาน


14

ฉันเขียนโค้ดในช่วงสุดสัปดาห์และพบว่าตัวเองต้องการเขียนโรงงานเป็นวิธีการคงที่ในคลาสฐาน

คำถามของฉันคือการรู้ว่า ac # idomatic approach หรือไม่?

ความรู้สึกของฉันที่อาจไม่ได้มาจากความจริงที่ว่าชั้นฐานมีความรู้เกี่ยวกับชั้นเรียนที่ได้รับ

ที่กล่าวว่าฉันไม่แน่ใจว่าวิธีที่ง่ายกว่าเพื่อให้ได้ผลลัพธ์เดียวกัน คลาสโรงงานอื่นทั้งดูเหมือน (อย่างน้อยฉัน) เช่นความซับซ้อนที่ไม่จำเป็น (?)

สิ่งที่ต้องการ:

class Animal
{
  public static Animal CreateAnimal(string name)
  {
     switch(name)
     {
        case "Shark":
          return new SeaAnimal();
          break;
        case "Dog":
          return new LandAnimal();
          break;
        default:
          throw new Exception("unknown animal");
     }
  }
}
class LandAnimal : Animal
{
}

class SeaAnimal : Animal
{
}

คุณจะทดสอบโรงงานของคุณอย่างไร?

ด้วยรหัสในคำถามนี้ฉันจะไม่ แต่คำตอบได้ช่วยให้ฉันตามเส้นทางของการบูรณาการการทดสอบมากขึ้นในรูปแบบการเขียนของฉัน
Aaron Anodide

พิจารณาว่าการดึงทั้ง Seaworld และ Landworld ในชั้นเรียนสัตว์ของคุณทำให้ยากต่อการจัดการในสภาพแวดล้อมที่แยกจากกัน

คำตอบ:


13

ข้อดีของคลาสโรงงานที่แยกจากกันคือสามารถเยาะเย้ยในการทดสอบหน่วย

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


ขอบคุณคุณช่วยยกตัวอย่างสิ่งที่คุณพูดถึงเมื่อคุณพูด polymorphic ด้วยวิธีอื่นได้ไหม?
Aaron Anodide

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

18

คุณสามารถใช้ Generics เพื่อหลีกเลี่ยงคำสั่ง switch และแยกการใช้งานดาวน์สตรีมจากคลาสฐานเช่นกัน:

 public static T CreateAnimal<T>() where T: new, Animal
 {
    return new T();
 }

การใช้งาน:

LandAnimal rabbit = Animal.CreateAnimal();  //Type inference should should just figure out the T by the return indicated.

หรือ

 var rabbit = Animal.CreateAnimal<LandAnimal>(); 


2
@PeterK ฟาวเลอร์อ้างถึงการเปลี่ยนข้อความใน Code Smells แต่โซลูชันของเขาคือพวกเขาควรถูกดึงไปยังคลาสของโรงงานแทนที่จะถูกแทนที่ ที่กล่าวว่าหากคุณมักจะรู้ว่าชนิดที่เวลาการพัฒนายาชื่อสามัญเป็นทางออกที่ดียิ่งขึ้น แต่ถ้าคุณทำไม่ได้ (ตามตัวอย่างดั้งเดิมบอกเป็นนัย) ฉันจะโต้แย้งว่าการใช้การสะท้อนเพื่อหลีกเลี่ยงการสลับในวิธีการจากโรงงานอาจเป็นเศรษฐกิจที่ผิดพลาด
pdr

เปลี่ยน statement และ if-else-if chains เหมือนกัน ฉันใช้ตัวเก่าเพราะการตรวจสอบเวลาคอมไพล์ที่บังคับให้ใช้ตัวพิมพ์ใหญ่
Aaron Anodide

4
@PeterK ในคำสั่งเปลี่ยนรหัสอยู่ในที่เดียว ใน OO แบบเต็มรหัสเดียวกันอาจแพร่กระจายผ่านหลายชั้นเรียนแยกต่างหาก มีพื้นที่สีเทาซึ่งความซับซ้อนที่เพิ่มขึ้นของการมีตัวอย่างจำนวนมากเป็นที่ต้องการน้อยกว่าคำสั่งเปลี่ยน

@Thorbjorn ฉันมีความคิดที่แน่นอน แต่ไม่เคยรัดกุมขอบคุณสำหรับการพูด
Aaron Anodide

5

เมื่อถึงจุดสุดขีดโรงงานอาจเป็นเรื่องธรรมดาเช่นกัน

interface IFactory<K, T> where K : IComparable
{
    T Create(K key);
}

จากนั้นหนึ่งสามารถสร้างโรงงานประเภทของวัตถุใด ๆ ซึ่งจะสามารถสร้างวัตถุชนิดใดก็ได้ ฉันไม่แน่ใจว่ามันง่ายกว่าหรือเปล่า

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


1

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

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

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


0

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

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

ที่นำไปสู่การพึ่งพาไฟล์แบบวงกลม - LandAnimal มาจากสัตว์ แต่สัตว์จำเป็นต้องรู้ว่ามี LandAnimal, SeaAnimal และอีกสิบห้าคลาส ฉันดึงวิธีการของโรงงานออกมาวางไว้ในไฟล์ของตัวเอง (ในแอปพลิเคชันหลักไม่ใช่คลังข้อมูลสัตว์ของฉัน) การใช้วิธีการแบบคงที่จากโรงงานดูน่ารักและฉลาด แต่ฉันรู้ว่ามันไม่ได้แก้ปัญหาการออกแบบจริงๆ

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

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

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