สลับกับความแตกต่างเมื่อจัดการกับรูปแบบและมุมมอง


12

ฉันไม่สามารถหาทางออกที่ดีกว่าสำหรับปัญหาของฉัน ฉันมีตัวควบคุมมุมมองที่นำเสนอรายการองค์ประกอบ องค์ประกอบเหล่านั้นเป็นแบบจำลองที่สามารถเป็นตัวอย่างของ B, C, D, ฯลฯ และสืบทอดจาก A. ดังนั้นในตัวควบคุมมุมมองแต่ละรายการควรไปที่หน้าจอที่แตกต่างกันของแอปพลิเคชันและส่งผ่านข้อมูลบางส่วนเมื่อผู้ใช้เลือกหนึ่งในนั้น . ทางเลือกสองทางที่อยู่ในใจของฉันคือ (กรุณาเพิกเฉยไวยากรณ์ไม่ใช่ภาษาที่เฉพาะเจาะจง)

1) สวิตช์ (ฉันรู้ว่า sucks)

//inside the view controller
void onClickItem(int index) {
    A a = items.get(index);

    switch(a.type) {
         case b:
             B b = (B)a;
             go to screen X;
             x.v1 = b.v1; // fill X with b data
             x.v2 = b.v2; 
         case c:
             go to screen Y;
         etc...
    }
}

2) ความหลากหลาย

//inside the view controller
void onClickItem(int index) {
    A a = items.get(index);
    Screen s = new (a.getDestinationScreen()); //ignore the syntax
    s.v1 = a.v1;   // fill s with information about A
    s.v2 = a.v2;
    show(s);
}

//inside B
Class getDestinationScreen(void) {
    return Class(X);
}

//inside C
Class getDestinationScreen(void) {
    return Class(Y);
}

ปัญหาของฉันกับโซลูชัน 2 คือเนื่องจาก B, C, D และอื่น ๆ เป็นรุ่นพวกเขาไม่ควรรู้เกี่ยวกับสิ่งที่เกี่ยวข้องกับการดู หรือพวกเขาควรในกรณีที่?

คำตอบ:


6

ฉันคิดว่าการใช้รูปแบบผู้เข้าชมอาจเป็นประโยชน์ที่นี่ คลาส B, C และ D จะต้อง "เข้าชม" เพื่อกำหนดประเภทมุมมอง แต่ไม่จำเป็นต้องรู้อะไรเกี่ยวกับมุมมอง ViewFactory (ด้านล่าง) จะไปที่รายการและใช้ polymorphism เพื่อกำหนดมุมมองที่ถูกต้องในการสร้าง ไม่มีคำสั่งสวิตช์ ไม่ถามเกี่ยวกับตัวแบบจำลองภายในเพื่อตัดสินใจว่าจะสร้างอะไร ส่วนต่อประสานผู้เยี่ยมชมใช้ polymorphism เพื่อเลือก setter ที่ถูกต้องสำหรับมุมมอง ตัวตั้งค่าสามารถส่งรายการไปยังตัวสร้างของประเภทมุมมองเฉพาะ (X หรือ Y หรือ Z) และมุมมองนั้นสามารถเติมข้อมูลในฟิลด์จากรายการ

   //inside the view controller
   void onClickItem(int index) {
      ViewFactoryVisitable a = items.get(index);
      ViewFactory aViewFactory = new ViewFactory(
      s = aViewFactory.getViewFor(a);
      show(s);
   }

--------

//Element interface
public interface ViewFactoryVisitable
{
    public void accept(ViewFactory theViewFactory);
}

---------

public interface ViewFactoryVisitor
{
   // one for each concrete type, polymorphism will choose correct setter
   public set setViewFor(B b);
   public set setViewFor(C c);
   public set setViewFor(D d);
}

--------

// B, C, D must implement this visitable interface
class B implements ViewFactoryVisitable
{ 
   ...

   //accept the ViewFactory as a visitor
   public void accept(ViewFactoryVisitor theViewFactoryVisitor)
   {
      theViewFactoryVisitor. setViewFor(this);
   }

   ...
} 

--------

class ViewFactory implements ViewFactoryVisitor
{
   ViewFactory(ViewFactoryVisitable theItem) {
      theItem.accept(this);
   }

   private View mView = null;
   ...

   public void setViewFor(B b) {
      // construct a view x and populate with data from b
      mView = new ViewX(b); 
   }

   public void setViewFor(C c) {
      mView = new ViewY(c); 
   }

   public void setViewFor(D d) {
      mView = new ViewZ(d); 
   }

   View getView() {
      return mView;
   }

} 

1
หากการดำเนินการยอมรับไม่เป็น "theViewFactoryVisitor.setViewFor (นี่);" ขออภัยถ้าฉันโง่!
Ryan

@ Ryan ดีจับ ความผิดพลาดนี้เกิดขึ้นที่นี่ 3 ปี!
Chuck Krutsinger

1

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

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

ฉันลงเอยด้วยการแบ่งชั้นให้โครงสร้างสองชั้นแบบขนาน คลาส Wanderer เป็นอิสระจากการรู้เกี่ยวกับกราฟิก แต่คลาส DrawWanderer ยังจำเป็นต้องรู้เพิ่มเติมเกี่ยวกับ Wanderer มากกว่าดีและจำเป็นต้องรู้เกี่ยวกับสภาพแวดล้อมกราฟิกที่แตกต่างกันสอง (และอาจมากกว่า) อย่างสมบูรณ์ (Views) (ฉันคิดว่าความคิดแยก - the - class นี้อาจเป็นคำตอบของแปลก ๆ แต่ทั้งหมดที่มันไม่จริงมีปัญหาเล็กน้อย)

ฉันคิดว่านี่เป็นปัญหาทั่วไปและพื้นฐานของการออกแบบ Object Oriented


0

ฉันคิดว่าการใช้สวิตช์เป็นตัวเลือกที่ดีกว่าการใช้ polymorphism ในกรณีนี้

มันค่อนข้างง่ายที่จะทำดังนั้นฉันไม่คิดว่ามันจะต้องมีความซับซ้อนโดยใช้ polymorphism

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


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

1
ไม่ต้องกังวลเพียงแค่ให้ 2 เซ็นต์ของฉันในเรื่อง แม้ว่าการแก้ปัญหาของคุณด้วยการใส่ตรรกะการดูลงในแบบจำลองของคุณคุณสามารถห่อมันด้วยเครื่องมือตกแต่งเพื่อให้แบบจำลองของคุณปราศจากตรรกะการดู จากนั้นคุณสามารถใช้ polymorphism กับคลาส decorator แทนโมเดล
Maru

0

ปัญหาของฉันกับโซลูชัน 2 คือเนื่องจาก B, C, D และอื่น ๆ เป็นรุ่นพวกเขาไม่ควรรู้เกี่ยวกับสิ่งที่เกี่ยวข้องกับการดู

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

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

ถ้าคุณทำให้คอมโบบ็อกซ์มีรายการสิ่งที่a.typeส่งคืนสิ่งใดคำสั่ง switch จะดูเป็นธรรมชาติมากขึ้น ScreenFactoryอย่างไรก็ตามแทนที่จะวางไว้ที่เหมาะสมในการจัดการเหตุการณ์คผมจะใส่ไว้ใน จากนั้นคุณมี:

//inside the view controller
void onClickItem(int index) {
    A a = items.get(index);

    s = _screenFactory.GetScreen(a);
    show(s);
    }
}

//inside a ScreenFactory implementation
internal Screen GetScreen(A typeIndicator)
{
switch(a.type) {
     case b:
         return new ScreenX();
     case c:
         return new ScreenY();
     etc...        
}

ซึ่งช่วยให้คุณทดสอบพฤติกรรมการสร้างหน้าจอและดึงฟังก์ชันการทำงานบางอย่างออกจาก UI ของคุณ มันทำให้มุมมองของคุณฝังอยู่ในสภาพเดิม บางทีมันอาจทำให้การออกแบบของคุณง่ายขึ้นหากหมายถึงAและคลาสย่อยสามารถยุบลงในtypeแฟล็กที่มี


ขอบคุณสำหรับการตอบสนองสูง น่าเสียดายที่รุ่นมีข้อมูลจำนวนมากไม่เพียง แต่ประเภทหรือส่วนควบคุมมุมมองปลายทาง นอกจากนี้แม้ว่าฉันไม่ได้พูดถึง แต่ ScreenX, ScreenY ฯลฯ จำเป็นต้องได้รับข้อมูลเกี่ยวกับ B, C, D ในการก่อสร้างดังนั้นฉันจึงไม่สามารถใช้โรงงานที่ผ่านประเภทเท่านั้นฉันต้องผ่านแบบจำลองเอง
Raphael Oliveira
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.