วิธีสร้างประเภทข้อมูลสำหรับสิ่งที่แสดงถึงตัวมันเองหรืออีกสองอย่าง


16

พื้นหลัง

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

บัตร

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

interface Card {
    String name();
    String text();
    // etc
}

มีคลาสย่อยสองชั้นCardซึ่งฉันกำลังเรียกPartialCard(ครึ่งหนึ่งของการ์ดสองส่วน) และWholeCard(การ์ดปกติ) PartialCardมีสองวิธีเพิ่มเติม: และPartialCard otherPart()boolean isFirstPart()

สภาผู้แทนราษฎร

ถ้าฉันมีดาดฟ้ามันควรจะประกอบด้วยWholeCards ไม่ใช่Cards อย่างที่Cardควรจะเป็นPartialCardและนั่นก็ไม่สมเหตุสมผล ดังนั้นฉันต้องการวัตถุที่แสดงถึง "บัตรทางกายภาพ" นั่นคือสิ่งที่สามารถเป็นตัวแทนหนึ่งWholeCardหรือสองPartialCards ฉันไม่แน่นอนโทรประเภทนี้Representativeและจะมีวิธีการที่Card จะให้ข้อมูลที่เกือบจะไม่มีโดยตรงบนบัตร (s) มันหมายถึงมันเท่านั้นที่จะชี้ไปที่มัน / พวกเขา ตอนนี้คิดที่ยอดเยี่ยม / บ้า / ใบ้ของฉัน (คุณตัดสินใจ) เป็นที่สืบทอด WholeCard จากทั้งสองและ ท้ายที่สุดพวกเขาเป็นการ์ดที่แสดงตัวของพวกเขาเอง! WholeCards สามารถใช้เป็นgetRepresentative()Representative CardRepresentativegetRepresentativereturn this;

สำหรับPartialCardsพวกเขาไม่ได้เป็นตัวแทนของตัวเอง แต่พวกเขามีภายนอกRepresentativeที่ไม่ได้เป็นCardแต่มีวิธีการในการเข้าถึงทั้งสองPartialCards

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

ความจำเป็นของการหล่อแบบ

เพราะPartialCardและWholeCardsมีทั้งCards Collection<Card>และมีเหตุผลที่มักจะไม่ดีที่จะแยกพวกเขาออกมาผมได้ตามปกติเพียงแค่ได้ร่วมงานกับ ดังนั้นบางครั้งฉันจำเป็นต้องส่งPartialCardเพื่อเข้าถึงวิธีการเพิ่มเติม ตอนนี้ฉันกำลังใช้ระบบที่อธิบายไว้ที่นี่เพราะฉันไม่ชอบนักแสดงที่ชัดเจน และชอบCard, Representativeจะต้องถูกทิ้งไปอย่างใดอย่างหนึ่งWholeCardหรือCompositeการเข้าถึงที่เกิดขึ้นจริงCardของพวกเขาเป็นตัวแทน

ดังนั้นเพียงเพื่อสรุป:

  • ประเภทฐาน Representative
  • ประเภทฐาน Card
  • ประเภทWholeCard extends Card, Representative(ไม่จำเป็นต้องเข้าถึงมันแสดงถึงตัวเอง)
  • ประเภทPartialCard extends Card(ให้การเข้าถึงส่วนอื่น ๆ )
  • ประเภทComposite extends Representative(ให้การเข้าถึงทั้งสองส่วน)

นี่มันบ้าเหรอ? ฉันคิดว่ามันสมเหตุสมผลจริงๆ แต่ฉันก็ไม่แน่ใจจริงๆ


1
PartialCards การ์ดมีประสิทธิภาพนอกเหนือจากนั้นมีสองต่อบัตรทางกายภาพ? คำสั่งที่พวกเขาเล่นนั้นสำคัญหรือไม่? คุณไม่สามารถสร้างคลาส "Deck Slot" และ "WholeCard" และอนุญาตให้ DeckSlots มี WholeCards หลายชุดและทำบางอย่างเช่น DeckSlot.WholeCards.Play และหากมี 1 หรือ 2 เล่นทั้งคู่ราวกับว่าพวกเขาแยกกัน บัตร? ดูเหมือนว่าได้รับการออกแบบที่มีความซับซ้อนมากขึ้นของคุณมากต้องมีบางอย่างที่ฉันหายไป :)
enderland

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

1
จริงๆแล้วฉันคิดว่ามันซับซ้อนอย่างน่าขัน ดูเหมือนว่าคุณจะมี แต่ความสัมพันธ์แบบ "เท่านั้น" ซึ่งนำไปสู่การออกแบบที่เข้มงวดมากกับการมีเพศสัมพันธ์อย่างแน่นหนา ที่น่าสนใจกว่านั้นคือสิ่งที่การ์ดเหล่านั้นกำลังทำอยู่จริง
Daniel Jour

2
ถ้าพวกเขาเป็น "แค่วัตถุข้อมูล" สัญชาตญาณของฉันก็จะมีหนึ่งคลาสที่มีอาร์เรย์ของวัตถุของคลาสที่สองและปล่อยไว้ที่นั้น ไม่มีการสืบทอดหรือภาวะแทรกซ้อนที่ไม่มีจุดหมายอื่น ๆ ไม่ว่าทั้งสองคลาสควรเป็น PlayableCard และ DrawableCard หรือ WholeCard และ CardPart ฉันก็ไม่รู้เพราะฉันไม่รู้ว่าเกมของคุณใช้งานได้ดีแค่ไหน แต่ฉันมั่นใจว่าคุณจะนึกถึงบางสิ่งที่จะเรียกมัน
Ixrec

1
พวกเขามีคุณสมบัติประเภทใด? คุณสามารถให้ตัวอย่างของการกระทำที่ใช้คุณสมบัติเหล่านี้ได้หรือไม่?
Daniel Jour

คำตอบ:


14

ดูเหมือนว่าฉันควรจะมีคลาสเหมือนกัน

class PhysicalCard {
    List<LogicalCard> getLogicalCards();
}

รหัสที่เกี่ยวข้องกับบัตรทางกายภาพสามารถจัดการกับชั้นบัตรทางกายภาพและรหัสที่เกี่ยวข้องกับบัตรทางตรรกะสามารถจัดการกับที่

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

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


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

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

8

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

ฉันขอแนะนำหนึ่งในสองทางเลือก:

ตัวเลือกที่ 1 รักษามันเป็นบัตรเดียวระบุว่าเป็นครึ่ง // ครึ่ง Bเช่นรายการไซต์ MTG สวม // ฉีกขาด แต่อนุญาตให้CardเอนทิตีของคุณมีNของแต่ละแอตทริบิวต์: playable-name, มานาราคา, ประเภท, หายาก, ข้อความ, เอฟเฟกต์ ฯลฯ

interface Card {
  List<String> Names();
  List<ManaCost> Costs();
  List<CardTypes> Types();
  /* etc. */
}

ตัวเลือกที่ 2ไม่ใช่ทั้งหมดที่แตกต่างจากตัวเลือกที่ 1 จำลองตามความเป็นจริงทางกายภาพ คุณมีCardเอนทิตีที่แสดงถึงบัตรจริง และวัตถุประสงค์ของมันเป็นแล้วจะถือไม่มี Playableสิ่ง บรรดาPlayableของแต่ละคนสามารถมีชื่อที่แตกต่างกันค่า mana รายการของผลกระทบของความสามารถในรายการ ฯลฯ .. และคุณ 'กาย' Cardสามารถมีตัวบ่งชี้ของตัวเอง (หรือชื่อ) ที่เป็นสารประกอบของแต่ละPlayableชื่อเหมือน ฐานข้อมูล MTG ปรากฏขึ้นให้ทำ

interface Card {
  String Name();
  List<Playable> Playables();
}

interface Playable {
  String Name();
  ManaCost Cost();
  CardType Type();
  /* etc. */
}

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


5

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

ประโยคนี้เป็นสัญญาณว่ามีบางอย่างผิดปกติในการออกแบบของคุณ: ใน OOP แต่ละคลาสควรมีบทบาทเดียวอย่างแน่นอนและการขาดพฤติกรรมจะเปิดเผยData Class ที่มีศักยภาพซึ่งเป็นกลิ่นที่ไม่ดีในรหัส

ท้ายที่สุดพวกเขาเป็นการ์ดที่แสดงตัวของพวกเขาเอง!

IMHO มันฟังดูแปลก ๆ และแปลกไปหน่อย วัตถุประเภท "บัตร" ควรเป็นตัวแทนของบัตร ระยะเวลา

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

สำหรับปัญหาที่คุณอธิบายฉันขอแนะนำรูปแบบการออกแบบ Compositแม้ว่าโดยทั่วไป DP นี้จะถูกนำเสนอเพื่อแก้ไขปัญหาทั่วไปมากขึ้น:

  1. สร้าง Cardส่วนต่อประสานตามที่คุณได้ทำไปแล้ว
  2. สร้าง a ConcreteCardที่ใช้Cardและกำหนดการ์ดใบหน้าอย่างง่าย อย่าลังเลที่จะทำให้พฤติกรรมปกติไพ่ในชั้นนี้
  3. สร้างCompositeCardที่ดำเนินการCardและมีสองเพิ่มเติม (และเบื้องต้นเอกชน) Cards ช่วยให้เรียกพวกเขาและleftCardrightCard

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

CompositeCardต้องใช้วิธีการที่ระบุไว้ในCardหลักสูตรและจะทำโดยคำนึงถึงความจริงที่ว่าการ์ดดังกล่าวทำจากไพ่ 2 ใบ (รวมถึงหากคุณต้องการสิ่งที่เฉพาะเจาะจงกับCompositeCardตัวการ์ดเองเช่นคุณอาจต้องการ การใช้งานต่อไปนี้:

public class CompositeCard implements Card
{ 
   private final Card leftCard, rightCard;
   private final double factor;

   @Override // Defined in Card
   public double attack(Player p){
      return factor * (leftCard.attack(p) + rightCard.attack(p));
   }

   @Override // idem
   public String name()
   {
       return leftCard.name() + " combined with " + rightCard.name();
   }

   ...
}

โดยการทำเช่นนั้นคุณสามารถใช้สิ่งCompositeCardที่คุณทำเพื่อสิ่งใด ๆCardและพฤติกรรมที่เฉพาะเจาะจงนั้นถูกซ่อนไว้ด้วยความแตกต่าง

หากคุณมีความมั่นใจว่าCompositeCardจะมีสองปกติCardของคุณสามารถเก็บความคิดและเพียงแค่ใช้ConcreateCardเป็นชนิดหาและ leftCardrightCard


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

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

@Spotted นี่ไม่ใช่รูปแบบของมัณฑนากรอย่างแน่นอนเนื่องจากเป็นคำที่ใช้ใน Java รูปแบบมัณฑนากรถูกนำมาใช้โดยวิธีการเอาชนะบนวัตถุแต่ละชิ้นสร้างคลาสที่ไม่ระบุชื่อที่ไม่ซ้ำกับวัตถุนั้น
Kevin Krumwiede

@KevinKrumwiede ตราบCompositeCardใดที่ไม่เปิดเผยวิธีการเพิ่มเติมCompositeCardเป็นเพียงมัณฑนากร
เห็น

"... รูปแบบการออกแบบช่วยให้คุณสามารถออกแบบองค์ประกอบระดับสูงได้ฟรี" - ไม่มันไม่ได้มาฟรีแต่ตรงกันข้าม - ราคาของการแก้ปัญหาที่ซับซ้อนกว่าความต้องการ
Doc Brown

3

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

บางทีการ์ดและเล่นได้มีผลกระทบ


น่าเสียดายที่ฉันไม่เล่นการ์ดเหล่านี้ พวกเขาเป็นเพียงวัตถุข้อมูลที่สามารถสอบถามได้ ถ้าคุณต้องการโครงสร้างข้อมูลสำรับคุณต้องการ List <WholeCard> หรืออะไรบางอย่าง หากคุณต้องการค้นหาบัตรทันทีสีเขียวคุณต้องการรายการ <Card>
codebreaker

อ่าโอเค. ไม่เกี่ยวข้องกับปัญหาของคุณมากนัก ฉันควรลบ
Davislor

3

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

เริ่มต้นด้วยสิ่งที่เป็นนามธรรมที่สูงขึ้น, Cardอินเทอร์เฟซ:

public interface Card {
    public void accept(CardVisitor visitor);
}

อาจมีพฤติกรรมเล็กน้อยบนCardอินเทอร์เฟซ แต่ตัวกระจายทรัพย์สินส่วนใหญ่ย้ายไปที่คลาสใหม่CardProperties:

public class CardProperties {
    // property methods, constructors, etc.

    String name();
    String text();
    // ...
}

ตอนนี้เราสามารถเป็นSimpleCardตัวแทนของไพ่ทั้งใบที่มีคุณสมบัติชุดเดียว:

public class SimpleCard implements Card {
    private CardProperties properties;

    // Constructors, ...

    @Override
    public void accept(CardVisitor visitor) {
        visitor.visit(properties);
    }
}

เรามาดูกันว่าCardPropertiesและและยังจะถูกเขียนCardVisitorเริ่มเข้ากันได้อย่างไรลองทำ a CompoundCardเพื่อเป็นตัวแทนของการ์ดที่มีสองหน้า:

public class CompoundCard implements Card {
    private CardProperties firstFaceProperties;
    private CardProperties secondFaceProperties;

    // Constructors, ...

    public void accept(CardVisitor visitor) {
        visitor.visit(firstFaceProperties, secondFaceProperties);
    }
}

การCardVisitorเริ่มปรากฏตัว ลองเขียนอินเตอร์เฟสนั้นตอนนี้:

public interface CardVisitor {
    public void visit(CardProperties properties);
    public void visit(CardProperties firstFaceProperties, CardProperties secondFaceProperties);
}

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

ตอนนี้เรามีชิ้นส่วนทั้งหมดแล้ว ตอนนี้เราแค่ต้องรวมมันเข้าด้วยกัน:

List<Card> cards = new LinkedList<>();
cards.add(new SimpleCard(new CardProperties(/* ... */)));
cards.add(new CompoundCard(new CardProperties(/* ... */), new CardProperties(/* ... */)));

 for(Card card : cards) {
     card.accept(new CardVisitor() {
         @Override
         public void visit(CardProperties properties) {
             // Do something for simple cards with a single face
         }

         public void visit(CardProperties firstFaceProperties, CardProperties secondFaceProperties) {
             // Do something else for compound cards with two faces
         }
     });
 }

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

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


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

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


1
ฉันคิดว่านี่สามารถขยายไปยังการ์ดที่ต้องเผชิญกับอีกสองชนิดได้อย่างง่ายดาย (ด้านหน้า & ด้านหลังแทนที่จะอยู่เคียงข้างกัน) ++
RubberDuck

สำหรับการสำรวจเพิ่มเติมการใช้ Visitor สำหรับการใช้ประโยชน์จากวิธีการที่เฉพาะเจาะจงกับคลาสย่อยบางครั้งเรียกว่า Multiple Dispatch Double Dispatch อาจเป็นวิธีแก้ปัญหาที่น่าสนใจ
mgoeminne

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

@Spotted ผู้เยี่ยมชมเป็นรูปแบบที่ซับซ้อนและฉันยังพิจารณารูปแบบผสมเมื่อเขียนคำตอบ เหตุผลที่ฉันไปกับผู้เยี่ยมชมเป็นเพราะ OP ต้องการปฏิบัติต่อสิ่งที่คล้ายกันแตกต่างกันมากกว่าสิ่งที่ต่างกันในทำนองเดียวกัน หากการ์ดมีพฤติกรรมมากขึ้นและถูกนำมาใช้สำหรับการเล่นเกมแล้วรูปแบบคอมโพสิตช่วยให้การรวมข้อมูลเพื่อสร้างสถิติแบบครบวงจร แต่เป็นเพียงข้อมูลที่ใช้ในการเรนเดอร์การ์ดแสดงผลซึ่งในกรณีนี้การรับข้อมูลที่แยกออกมาดูเหมือนจะมีประโยชน์มากกว่าคอมโพสิตแบบเรียบง่าย
cbojar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.