किसी ऑब्जेक्ट को उसके प्रस्तुतकर्ता को मैप करने का OOP तरीका साफ़ करें


13

मैं जावा में एक बोर्ड गेम (जैसे शतरंज) बना रहा हूं, जहां प्रत्येक टुकड़ा का अपना प्रकार (जैसे Pawn, Rookआदि) है। आवेदन के जीयूआई भाग के लिए मुझे इनमें से प्रत्येक टुकड़े के लिए एक छवि की आवश्यकता है। चूंकि सोच समझ कर करते हैं

rook.image();

UI और व्यावसायिक तर्क के पृथक्करण का उल्लंघन करता है, मैं प्रत्येक टुकड़े के लिए एक अलग प्रस्तोता बनाऊंगा और फिर उनके समान अंशों के लिए टुकड़ा प्रकारों का मानचित्र तैयार करूंगा

private HashMap<Class<Piece>, PiecePresenter> presenters = ...

public Image getImage(Piece piece) {
  return presenters.get(piece.getClass()).image();
}

अब तक सब ठीक है। हालांकि, मुझे लगता है कि एक विवेकपूर्ण ओओपी गुरु एक getClass()विधि को बुलाएगा और इस तरह उदाहरण के लिए एक आगंतुक का उपयोग करने का सुझाव देगा:

class Rook extends Piece {
  @Override 
  public <T> T accept(PieceVisitor<T> visitor) {
    return visitor.visitRook(this);
  }
}

class ImageVisitor implements PieceVisitor<Image> {
  @Override  
  public Image visitRook(Rook rook) {
    return rookImage;
  } 
}

मुझे यह समाधान पसंद है (धन्यवाद, गुरु), लेकिन इसकी एक महत्वपूर्ण कमी है। हर बार जब एक नया टुकड़ा टाइप किया जाता है, तो एप्लिकेशन को एक नए तरीके से अपडेट करने की आवश्यकता होती है। मैं एक बोर्ड गेम फ्रेमवर्क के रूप में अपने सिस्टम का उपयोग करना चाहूंगा जहां नए टुकड़े एक सरल प्रक्रिया के माध्यम से जोड़े जा सकते हैं जहां फ्रेमवर्क का उपयोगकर्ता केवल टुकड़ा और उसके प्रस्तुतकर्ता दोनों का कार्यान्वयन प्रदान करेगा, और बस इसे फ्रेमवर्क में प्लग कर देगा। मेरा प्रश्न: आदि के बिना एक साफ ओओपी समाधान है instanceof, getClass()जो इस तरह के विस्तार के लिए अनुमति देगा?


प्रत्येक प्रकार के शतरंज के टुकड़े के लिए खुद का वर्ग होने का क्या उद्देश्य है? मुझे लगता है कि एक टुकड़ा वस्तु स्थिति, रंग और एक टुकड़े का प्रकार रखती है। मैं शायद एक टुकड़ा वर्ग और दो enums (PieceColor, PieceType) उस उद्देश्य के लिए होगा।
से आ

@COMEFROM अच्छी तरह से, स्पष्ट रूप से अलग-अलग प्रकार के टुकड़े में अलग-अलग व्यवहार होते हैं, इसलिए कुछ कस्टम कोड होने की आवश्यकता है जो कहती हैं कि बदमाश और प्यादे के बीच अंतर होता है। उस ने कहा, मेरे पास आमतौर पर एक मानक टुकड़ा वर्ग होता है जो सभी प्रकारों को संभालता है और व्यवहार को अनुकूलित करने के लिए रणनीति वस्तुओं का उपयोग करता है।
जूल्स

@ जूल्स और प्रत्येक टुकड़ा के लिए एक अलग वर्ग होने के लिए रणनीति का क्या फायदा होगा जो अपने आप में व्यवहार वाले हर टुकड़े के लिए एक अलग वर्ग है?
lishaak

2
खेल के नियमों को अलग-अलग टुकड़ों से अलग करने का एक स्पष्ट लाभ यह है कि अलग-अलग टुकड़ों में यह है कि यह आपकी समस्या को तुरंत हल करता है। जब मैं बारी आधारित बोर्ड गेम को लागू करता हूं तो मैं आमतौर पर राज्य मॉडल के साथ नियम मॉडल को नहीं मिलाऊंगा।
आते हैं

2
यदि आप नियमों को अलग नहीं करना चाहते हैं, तो आप छह टुकड़ा टाइप वस्तुओं (नहीं कक्षाएं!) में आंदोलन नियमों को परिभाषित कर सकते हैं। वैसे भी, मुझे लगता है कि जिस तरह की समस्याओं का सामना करना पड़ रहा है, उससे बचने का सबसे अच्छा तरीका है कि आप चिंताओं को अलग करें और विरासत का उपयोग तभी करें जब यह वास्तव में उपयोगी हो।
से आ

जवाबों:


10

वहाँ एक स्वच्छ OOP समाधान है उदाहरण के लिए, getClass () आदि के बिना जो इस तरह की व्यापकता के लिए अनुमति देगा?

हाँ वहाँ है।

मैं इसके बारे में आपसे पूछता हूं। अपने वर्तमान उदाहरणों में आप टुकड़ों प्रकार के चित्रों को मैप करने के तरीके खोज रहे हैं। यह एक टुकड़े को स्थानांतरित करने की समस्या को कैसे हल करता है?

प्रकार के बारे में पूछने से अधिक शक्तिशाली तकनीक बताएं का पालन करना है, मत पूछो । क्या होगा यदि प्रत्येक टुकड़ा ने एक PiecePresenterइंटरफ़ेस लिया और यह इस तरह दिखे:

class PiecePresenter implements PieceOutput {

  BoardPresenter board;
  Image pieceImage;

  @Override
  PiecePresenter(BoardPresenter board, Image image) {
    public void display(int rank, int file) {
      board.display(pieceImage, rank, file);
    } 
  }
}

निर्माण कुछ इस तरह दिखेगा:

rookWhiteImage = new Image("Rook-White.png");
PieceOutput rookWhiteOutPort = new PiecePresenter(boardPresenter, rookWhiteImage);
PieceInput rookWhiteInPort = new Rook(rookWhiteOutPort);
board[0, 0] = rookWhiteInPort;

उपयोग कुछ इस तरह दिखेगा:

board[rank, file].display(rank, file);

यहां विचार यह है कि ऐसा कुछ भी करने के लिए ज़िम्मेदारी लेने से बचें जो इसके बारे में न पूछकर और न ही इसके आधार पर निर्णय लेने के लिए अन्य चीजें जिम्मेदार हैं। इसके बजाय किसी ऐसी चीज़ का संदर्भ रखें जो यह जानती हो कि किसी चीज़ के बारे में क्या करना है और यह बताएं कि आप जो जानते हैं उसके बारे में कुछ करें।

यह बहुरूपता के लिए अनुमति देता है। आप परवाह नहीं करते कि आप किससे बात कर रहे हैं। आपको परवाह नहीं है कि इसे क्या कहना है। आप बस इस बात की परवाह करते हैं कि यह वही कर सकता है, जिसकी आपको जरूरत है।

एक अच्छा चित्र है कि अलग परतों में इन रहता है, बता-इसे-पूछना इस प्रकार है, और शो कैसे अन्यायपूर्ण परत नहीं जोड़े परत करने के लिए है इस :

यहाँ छवि विवरण दर्ज करें

यह एक उपयोग केस लेयर जोड़ता है जिसका हमने यहां उपयोग नहीं किया है (और निश्चित रूप से जोड़ सकते हैं) लेकिन हम उसी पैटर्न का अनुसरण कर रहे हैं जिसे आप निचले दाएं कोने में देखते हैं।

आप यह भी देखेंगे कि प्रस्तुतकर्ता इनहेरिटेंस का उपयोग नहीं करता है। यह रचना का उपयोग करता है। बहुरूपता को प्राप्त करने के लिए वंशानुक्रम अंतिम रास्ता होना चाहिए। मैं रचना और प्रतिनिधिमंडल का उपयोग करने वाले डिजाइनों को पसंद करता हूं। यह थोड़ा अधिक कीबोर्ड टाइपिंग है, लेकिन यह बहुत अधिक शक्ति है।


2
मुझे लगता है कि यह शायद एक अच्छा उत्तर (+1) है, लेकिन मुझे यकीन नहीं है कि आपका समाधान स्वच्छ वास्तुकला का एक अच्छा उदाहरण है: रूक इकाई अब बहुत सीधे एक प्रस्तुतकर्ता के लिए एक संदर्भ रखती है। क्या यह इस तरह का युग्मन नहीं है जिसे स्वच्छ वास्तुकला रोकने की कोशिश कर रहा है? और आपका जवाब काफी स्पष्ट नहीं है: संस्थाओं और प्रस्तुतकर्ताओं के बीच की मैपिंग अब जो भी संस्था को तत्काल सौंप देती है। वैकल्पिक समाधानों की तुलना में यह संभवतः अधिक सुरुचिपूर्ण है, लेकिन नई संस्थाओं को जोड़ने पर यह संशोधित होने के लिए एक तीसरा स्थान है - अब, यह विज़िटर नहीं है, बल्कि एक कारखाना है।
आमोन

@amon, जैसा कि मैंने कहा, केस केस लेयर गायब है। टेरी प्रचेत ने इस तरह की चीजों को "बच्चों के लिए झूठ" कहा। मैं एक ऐसा उदाहरण नहीं बनाने की कोशिश कर रहा हूं जो जबरदस्त हो। अगर आपको लगता है कि मुझे स्कूल ले जाने की जरूरत है, जहां यह क्लीन आर्किटेक्चर की बात आती है, तो मैं आपको यहां काम करने के लिए ले जाने के लिए आमंत्रित करता हूं ।
कैंडिड_ऑरेंज

क्षमा करें, नहीं, मैं आपको स्कूल नहीं करना चाहता, मैं सिर्फ इस आर्किटेक्चर पैटर्न को बेहतर तरीके से समझना चाहता हूं। मैं सचमुच एक महीने से भी कम समय पहले "स्वच्छ वास्तुकला" और "हेक्सागोनल वास्तुकला" अवधारणाओं के पार आया था।
आमोन

उस मामले में @amon इसे एक अच्छा हार्ड लुक देते हैं और फिर मुझे काम पर ले जाते हैं। मुझे अच्छा लगा अगर तुमने किया। मैं अभी भी इसके कुछ हिस्सों का पता लगा रहा हूं। फिलहाल मैं इस शैली में एक मेनू संचालित अजगर परियोजना के उन्नयन पर काम कर रहा हूं। स्वच्छ वास्तुकला की एक महत्वपूर्ण समीक्षा यहां मिल सकती है
कैंडिड_ओरेंज

1
@ लिशाक तात्कालिकता मुख्य रूप से हो सकती है। आंतरिक परतों को बाहरी परतों के बारे में पता नहीं है। बाहरी परतों को केवल इंटरफेस के बारे में पता है।
कैंडिड_ऑरेंज

5

उसका क्या:

आपके मॉडल (आकृति वर्ग) में एक सामान्य तरीके हैं जिनकी आपको अन्य संदर्भ में भी आवश्यकता हो सकती है:

interface ChessFigure {
  String getPlayerColor();
  String getFigureName();
}

एक निश्चित आकृति को प्रदर्शित करने के लिए उपयोग की जाने वाली छवियां एक नामकरण स्कीमा द्वारा फ़ाइल नाम प्राप्त करती हैं:

King-White.png
Queen-Black.png

फिर आप जावा कक्षाओं के बारे में जानकारी प्राप्त किए बिना उपयुक्त छवि को लोड कर सकते हैं।

new File(FIGURE_IMAGES_DIR,
         String.format("%s-%s.png",
                       figure.getFigureName(),
                       figure.getPlayerColor)));

मुझे इस तरह की समस्याओं के लिए सामान्य समाधान में भी दिलचस्पी है जब मुझे कक्षाओं के संभावित सेट पर कुछ जानकारी (न केवल छवियों) को संलग्न करने की आवश्यकता होती है। "

मुझे लगता है कि आपको उस वर्ग पर ध्यान केंद्रित नहीं करना चाहिए । बल्कि व्यावसायिक वस्तुओं के संदर्भ में सोचते हैं

और जेनेरिक समाधान किसी भी प्रकार का मानचित्रण है। IMHO की चाल कोड से उस मैपिंग को एक संसाधन में स्थानांतरित करना है जिसे बनाए रखना आसान है।

मेरा उदाहरण इस मैपिंग को कन्वेंशन द्वारा करता है जो कि व्यवसाय मॉडल में दृश्य संबंधी जानकारी को जोड़ने के लिए लागू करने और बचने में काफी आसान है । दूसरी ओर आप इसे "छिपा हुआ" मानचित्रण मान सकते हैं क्योंकि यह कहीं भी व्यक्त नहीं किया गया है।

एक अन्य विकल्प यह है कि इसे अपने स्वयं के एमवीसी-परतों के साथ एक अलग व्यावसायिक मामले के रूप में देखा जाए, जिसमें एक दृढ़ता परत शामिल है जिसमें मैपिंग शामिल है।


मैं इसे इस विशेष परिदृश्य के लिए एक बहुत ही व्यावहारिक और डाउन-टू-अर्थ समाधान के रूप में देखता हूं। मुझे इस तरह की समस्याओं के लिए सामान्य समाधान में भी दिलचस्पी है जब मुझे कक्षाओं के संभावित बढ़ते सेट के लिए कुछ जानकारी (न केवल छवियों) को संलग्न करने की आवश्यकता होती है।
lishaak

3
@ लिसाक: यहां सामान्य दृष्टिकोण आपके व्यावसायिक वस्तुओं में पर्याप्त मेटा डेटा प्रदान करना है ताकि किसी संसाधन या उपयोगकर्ता इंटरफ़ेस तत्वों के लिए सामान्य मानचित्रण स्वचालित रूप से किया जा सके।
डॉक ब्राउन

2

मैं प्रत्येक जानकारी के लिए एक अलग यूआई / व्यू क्लास बनाऊंगा जिसमें दृश्य जानकारी हो। इन टुकड़ों में से हर एक वर्ग के पास अपने मॉडल / व्यापार समकक्ष के लिए एक संकेतक होता है जिसमें टुकड़ा की स्थिति और खेल के नियम होते हैं।

इसलिए उदाहरण के लिए एक मोहरा लें:

class Pawn : public Piece {
public:
    Vec2 position() const;
    /**
     The rest of the piece's interface
     */
}

class PawnView : public PieceView {
public:
    PawnView(Piece* piece) { _piece = piece; }
    void drawSelf(BoardView* board) const{
         board.drawPiece(_image, _piece->position);
    }
private:
    Piece* _piece;
    Image _image;
}

यह तर्क और UI के पूर्ण पृथक्करण की अनुमति देता है। आप लॉजिक पीस पॉइंटर को एक गेम क्लास में पास कर सकते हैं, जो टुकड़ों की गति को संभालता है। एकमात्र दोष यह है कि तात्कालिकता यूआई वर्ग में घटित होगी।


ठीक है, तो हम कहते हैं कि मेरे पास कुछ है Piece* p। मुझे कैसे पता चलेगा कि मुझे PawnViewइसे प्रदर्शित करने के लिए एक बनाना है , RookViewया नहीं KingView? या जब भी मैं एक नया टुकड़ा बनाऊं, मुझे तुरंत एक साथ देखने या प्रस्तुतकर्ता बनाना होगा? यह मूल रूप से निर्भरता के साथ @ CandiedOrange का समाधान उल्टा होगा। उस मामले में, PawnViewकंस्ट्रक्टर भी एक ले सकता है Pawn*, न कि केवल एक Piece*
आमोन

हाँ, मुझे खेद है, PawnView कंस्ट्रक्टर एक प्यादा * लेगा। और आप एक ही समय में एक PawnView और एक मोहरा बनाने के लिए जरूरी नहीं है। मान लीजिए कि एक खेल है जिसमें 100 प्यादा हो सकते हैं, लेकिन एक समय में केवल 10 दृश्य हो सकते हैं, इस मामले में आप कई प्यादों के लिए प्यादा साक्षात्कार का पुन: उपयोग कर सकते हैं।
लस जैकब्स

और मैं @ CandiedOrange के समाधान से सहमत हूं, हालांकि मैं अपने 2 सेंट साझा करूंगा।
लस जैकब्स

0

मैं इसे Pieceसामान्य बनाकर दृष्टिकोण करूंगा , जहां इसका पैरामीटर एक गणना का प्रकार है जो टुकड़े के प्रकार की पहचान करता है, प्रत्येक टुकड़ा एक ऐसे प्रकार का संदर्भ होता है। तब यूआई पहले की तरह एन्यूमरेशन से मैप का उपयोग कर सकता था:

public abstract class Piece<T>
{
    T type;
    public Piece (T type) { this.type = type; }
    public T getType() { return type; }
}
enum ChessPieceType { PAWN, ... }
public class Pawn extends Piece<ChessPieceType>
{
    public Pawn () { super (ChessPieceType.PAWN); }

इसके दो दिलचस्प फायदे हैं:

सबसे पहले, अधिकांश वैधानिक रूप से टाइप की जाने वाली भाषाओं पर लागू होता है: यदि आप अपने बोर्ड को xpect के टुकड़े के प्रकार के साथ जोड़ते हैं, तो आप उसमें गलत प्रकार का टुकड़ा नहीं डाल सकते।

दूसरा, और शायद अधिक दिलचस्प बात यह है कि यदि आप जावा (या अन्य JVM भाषाओं) में काम कर रहे हैं, तो आपको ध्यान देना चाहिए कि प्रत्येक enum मान केवल एक स्वतंत्र वस्तु नहीं है, बल्कि इसकी अपनी कक्षा भी हो सकती है। इसका मतलब यह है कि आप अपने टुकड़े प्रकार की वस्तुओं का उपयोग रणनीति वस्तुओं के रूप में कर सकते हैं ताकि ग्राहक का व्यवहार हो सके:

 public class ChessPiece extends Piece<ChessPieceType> {
    ....
   boolean isMoveValid (Move move)
    {
         return getType().movePatterns().contains (move.asVector()) && ....


 public enum ChessPieceType {
    public abstract Set<Vector2D> movePatterns();
    PAWN {
         public Set<Vector2D> movePatterns () {
              return Util.makeSet(
                    new Vector2D(0, 1),
                    ....

(स्पष्ट रूप से वास्तविक कार्यान्वयन की तुलना में अधिक जटिल होने की आवश्यकता है, लेकिन उम्मीद है कि आपको यह विचार मिलेगा)


0

मैं व्यावहारिक प्रोग्रामर हूं और मुझे वास्तव में परवाह नहीं है कि स्वच्छ या गंदा आर्किटेक्चर क्या है। मेरा मानना ​​है कि आवश्यकताओं और इसे सरल तरीके से नियंत्रित किया जाना चाहिए।

आपकी आवश्यकता है आपका शतरंज ऐप तर्क वेब, मोबाइल ऐप या यहां तक ​​कि कंसोल ऐप जैसे विभिन्न प्रस्तुति परतों (उपकरणों) पर प्रस्तुत किया जाएगा ताकि आपको इन आवश्यकताओं का समर्थन करने की आवश्यकता हो। आप प्रत्येक डिवाइस पर बहुत अलग रंगों, टुकड़ों की छवियों का उपयोग करना पसंद कर सकते हैं।

public class Program
{
    public static void Main(string[] args)
    {
        new Rook(new Presenter { Image = "rook.png", Color = "blue" });
    }
}

public abstract class Piece
{
    public Presenter Presenter { get; private set; }
    public Piece(Presenter presenter)
    {
        this.Presenter = presenter;
    }
}

public class Pawn : Piece
{
    public Pawn(Presenter presenter) : base(presenter) { }
}

public class Rook : Piece
{
    public Rook(Presenter presenter) : base(presenter) { }
}

public class Presenter
{
    public string Image { get; set; }
    public string Color { get; set; }
}

जैसा कि आपने देखा है कि प्रस्तोता पैरामीटर को प्रत्येक डिवाइस (प्रस्तुति परत) पर अलग तरीके से पारित किया जाना चाहिए। इसका मतलब है कि आपकी प्रस्तुति परत तय करेगी कि प्रत्येक टुकड़े का प्रतिनिधित्व कैसे किया जाए। इस समाधान में क्या गलत है?


अच्छी तरह से पहले, टुकड़ा को पता है कि प्रस्तुति की परत वहां है और इसे छवियों की आवश्यकता है। क्या होगा अगर कुछ प्रस्तुति परत को छवियों की आवश्यकता नहीं है? दूसरा, टुकड़ा को यूआई परत द्वारा तुरंत किया जाना है, क्योंकि एक टुकड़ा प्रस्तुतकर्ता के बिना मौजूद नहीं हो सकता है। कल्पना कीजिए कि गेम एक ऐसे सर्वर पर चलता है, जहाँ UI की आवश्यकता नहीं है। तब आप एक टुकड़े को तुरंत नहीं कर सकते क्योंकि आपके पास यूआई नहीं है।
20

आप रिपीटर को वैकल्पिक पैरामीटर के रूप में भी परिभाषित कर सकते हैं।
Freshblood

0

एक और उपाय है जो आपको UI और डोमेन लॉजिक को पूरी तरह से अमूर्त करने में मदद करेगा। आपके बोर्ड को आपके UI लेयर के संपर्क में आना चाहिए और आपकी UI लेयर यह तय कर सकती है कि टुकड़ों और पदों का प्रतिनिधित्व कैसे करें।

इसे प्राप्त करने के लिए, आप फेन स्ट्रिंग का उपयोग कर सकते हैं । फेन स्ट्रिंग मूल रूप से राज्य की जानकारी है और यह बोर्ड पर वर्तमान टुकड़े और उनके स्थान देता है। तो आपके बोर्ड में एक विधि हो सकती है जो बोर्ड की वर्तमान स्थिति को फेन स्ट्रिंग के माध्यम से लौटाती है तो आपकी यूआई परत बोर्ड का प्रतिनिधित्व कर सकती है। यह वास्तव में वर्तमान शतरंज इंजन कैसे काम करता है। शतरंज इंजन जीयूआई के बिना कंसोल अनुप्रयोग हैं लेकिन हम बाहरी जीयूआई के माध्यम से उनका उपयोग करते हैं। शतरंज इंजन फेन स्ट्रिंग्स और शतरंज संकेतन के माध्यम से GUI को संचार करता है।

आप पूछ रहे हैं कि क्या होगा अगर मैं एक नया टुकड़ा जोड़ूं? यह यथार्थवादी नहीं है कि शतरंज एक नया टुकड़ा पेश करेगा। यह आपके डोमेन में बहुत बड़ा बदलाव होगा। इसलिए YAGNI सिद्धांत का पालन करें।


खैर, यह समाधान निश्चित रूप से कार्यात्मक है और मैं इस विचार की सराहना करता हूं। हालाँकि, यह शतरंज तक ही सीमित है। मैंने सामान्य समस्या को स्पष्ट करने के लिए एक उदाहरण के रूप में शतरंज का अधिक उपयोग किया (मैंने प्रश्न में यह स्पष्ट किया है)। आपके द्वारा प्रस्तावित समाधान का उपयोग अन्य डोमेन में नहीं किया जा सकता है और जैसा कि आप सही ढंग से कहते हैं, नए व्यापार ऑब्जेक्ट (टुकड़े) के साथ इसे विस्तारित करने का कोई तरीका नहीं है। वहाँ वास्तव में अधिक टुकड़ों के साथ शतरंज का विस्तार करने के लिए कई तरीके हैं ....
lishaak
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.