ใช้รูปแบบผู้เข้าชมที่มีลำดับชั้นวัตถุขนาดใหญ่


12

บริบท

ฉันได้ใช้กับลำดับชั้นของวัตถุ (ต้นไม้นิพจน์) รูปแบบผู้เข้าชม "หลอก" (หลอกเพราะมันไม่ได้ใช้ส่งสองครั้ง):

 public interface MyInterface
 {
      void Accept(SomeClass operationClass);
 }

 public class MyImpl : MyInterface 
 {
      public void Accept(SomeClass operationClass)
      {   
           operationClass.DoSomething();
           operationClass.DoSomethingElse();
           // ... and so on ...
      }
 }

การออกแบบนี้เป็นที่ตั้งคำถาม แต่ค่อนข้างสะดวกสบายเนื่องจากจำนวนการใช้งานของ MyInterface มีความสำคัญ (~ 50 หรือมากกว่า) และฉันไม่จำเป็นต้องเพิ่มการดำเนินการเพิ่มเติม

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

Traversal ปัจจุบันดำเนินการโดยการเรียกการยอมรับการดำเนินการบนโหนดรูทของต้นไม้ซึ่งจะเรียกสายยอมรับในแต่ละโหนดลูกซึ่งในทางกลับกัน ... และอื่น ๆ ...

แต่ถึงเวลาแล้วที่ฉันต้องเพิ่มการทำงานใหม่เช่นการพิมพ์ที่สวยงาม

 public class MyImpl : MyInterface 
 {
      // Property does not come from MyInterface
      public string SomeProperty { get; set; }

      public void Accept(SomeClass operationClass)
      {   
           operationClass.DoSomething();
           operationClass.DoSomethingElse();
           // ... and so on ...
      }

      public void Accept(SomePrettyPrinter printer)
      {
           printer.PrettyPrint(this.SomeProperty);
      }
 }    

โดยทั่วไปฉันเห็นสองตัวเลือก:

  • คงการออกแบบเดิมโดยเพิ่มวิธีการใหม่สำหรับการทำงานของฉันในแต่ละคลาสที่ได้รับโดยมีค่าใช้จ่ายในการบำรุงรักษา (ไม่ใช่ตัวเลือก IMHO)
  • ใช้รูปแบบผู้เข้าชม "จริง" โดยเสียค่าใช้จ่ายในการขยาย (ไม่ใช่ตัวเลือกเนื่องจากฉันคาดว่าจะมีการใช้งานเพิ่มเติมตามทาง ... ) โดยมีวิธีการเยี่ยมชมประมาณ 50+ แต่ละวิธีที่ตรงกับการใช้งานเฉพาะ ?

คำถาม

คุณจะแนะนำและใช้รูปแบบผู้เข้าชมหรือไม่ มีรูปแบบอื่นที่สามารถช่วยแก้ปัญหานี้ได้หรือไม่?


1
อาจจะเป็นโซ่ของนักตกแต่งที่เหมาะสมกว่า
MattDavey

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

@MattDavey: ดังนั้นคุณจะแนะนำและมีหนึ่งมัณฑนากรต่อการใช้งานและการดำเนินงานหรือไม่
ต. ฟาเบร

2
@ T.Fabre มันยากที่จะบอก มี 50 + implementors ของมีMyInterface.. ไม่ทุกชั้นเรียนเหล่านั้นมีการดำเนินงานที่ไม่ซ้ำกันของDoSomethingและDoSomethingElse? ฉันไม่เห็นว่าที่ชั้นผู้เข้าชมของคุณไปตามลำดับชั้นจริงๆ - ดูเหมือนว่าfacadeในขณะนี้ ..
MattDavey

รุ่น C # คืออะไร คุณมีลูกแกะหรือไม่ หรือ linq ตามที่คุณต้องการ
jk

คำตอบ:


13

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

อย่าใช้เกินพิกัดในส่วนต่อประสานของผู้เยี่ยมชม

ใส่ประเภทลงในชื่อวิธีการเช่นการใช้งาน

IExpressionVisitor {
    void VisitPrimitive(IPrimitiveExpression expr);
    void VisitComposite(ICompositeExpression expr);
}

ค่อนข้างมากกว่า

IExpressionVisitor {
    void Visit(IPrimitiveExpression expr);
    void Visit(ICompositeExpression expr);
}

เพิ่มวิธีการ "ที่ไม่รู้จัก" ไปยังส่วนต่อประสานผู้เยี่ยมชมของคุณ

มันจะทำให้เป็นไปได้สำหรับผู้ใช้ที่ไม่สามารถแก้ไขรหัสของคุณ:

IExpressionVisitor {
    void VisitPrimitive(IPrimitiveExpression expr);
    void VisitComposite(ICompositeExpression expr);
    void VisitExpression(IExpression expr);
};

สิ่งนี้จะช่วยให้พวกเขาสร้างการใช้งานของตนเองIExpressionและIVisitor"เข้าใจ" นิพจน์ของพวกเขาโดยใช้ข้อมูลชนิดเวลาทำงานในการใช้VisitExpressionวิธีการcatch-all

จัดทำIVisitorอินเทอร์เฟซที่ไม่ต้องทำอะไรเป็นค่าเริ่มต้น

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


2
คุณช่วยอธิบายได้ไหมว่าทำไมคุณถึงพูดDo not use overloads in the interface of the visitor?
Steven Evers

1
คุณช่วยอธิบายได้ไหมว่าทำไมคุณถึงไม่แนะนำและใช้งานเกินพิกัด? ฉันอ่านบางแห่ง (บน oodesign.com จริง ๆ ) ว่ามันไม่สำคัญว่าฉันจะใช้งานเกินพิกัดหรือไม่ มีเหตุผลเฉพาะใด ๆ ที่ทำให้คุณชอบการออกแบบนั้นมากขึ้น?
ต. ฟาเบร

2
@ T.Fabre มันไม่สำคัญในเรื่องของความเร็ว แต่มันมีความสำคัญในแง่ของการอ่าน การแก้ไขวิธีการในสองในสามภาษาที่ฉันใช้งานนี้ ( Javaและ C #) ต้องใช้ขั้นตอนการทำงานเพื่อเลือกระหว่างการโอเวอร์โหลดที่อาจเกิดขึ้นทำให้โค้ดที่มีการโอเวอร์โหลดจำนวนมากยากต่อการอ่าน การปรับโครงสร้างโค้ดอีกครั้งจะกลายเป็นเรื่องง่ายขึ้นด้วยเช่นกันเพราะการเลือกวิธีที่คุณต้องการแก้ไขกลายเป็นเรื่องไม่สำคัญ
dasblinkenlight

@SnOrfus โปรดดูคำตอบของฉันต่อ T.Fabre ด้านบน
dasblinkenlight

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