জাভাতে উদাহরণ এড়ানো হচ্ছে


102

"উদাহরণস্বরূপ" অপারেশনগুলির একটি শৃঙ্খলা থাকা "কোড গন্ধ" হিসাবে বিবেচিত। মানক উত্তরটি হ'ল "পলিমারফিজম ব্যবহার করুন"। এই ক্ষেত্রে আমি এটি কীভাবে করব?

বেস ক্লাসের বেশ কয়েকটি সাবক্লাস রয়েছে; তাদের কেউই আমার নিয়ন্ত্রণে নেই। একটি সাদৃশ্যপূর্ণ পরিস্থিতি জাভা শ্রেণীর পূর্ণসংখ্যা, ডাবল, বিগডিসিমাল ইত্যাদির সাথে be

if (obj instanceof Integer) {NumberStuff.handle((Integer)obj);}
else if (obj instanceof BigDecimal) {BigDecimalStuff.handle((BigDecimal)obj);}
else if (obj instanceof Double) {DoubleStuff.handle((Double)obj);}

নাম্বার স্টাফ ইত্যাদির উপর আমার নিয়ন্ত্রণ আছে।

আমি কোডের অনেকগুলি লাইন ব্যবহার করতে চাই না যেখানে কয়েকটি লাইন করত। (কখনও কখনও আমি ইন্টিজার স্টাফ, বিগডিসিমাল.ক্লাসের বিগডিসিমাল স্টফ ইত্যাদির উদাহরণ হিসাবে একটি হ্যাশম্যাপ ম্যাপিং ইন্টিজারক্লাস তৈরি করি তবে আজ আমি আরও সহজ কিছু চাই))

আমি এর মতো সহজ কিছু চাই:

public static handle(Integer num) { ... }
public static handle(BigDecimal num) { ... }

তবে জাভা ঠিক সেভাবে কাজ করে না।

ফর্ম্যাট করার সময় আমি স্থির পদ্ধতি ব্যবহার করতে চাই। আমি যে জিনিসগুলি ফর্ম্যাট করছি সেগুলি সম্মিলিত, যেখানে একটি থিং 1 এ অ্যারে থিং 2 এবং থিং 2 এ থিং 1 এর অ্যারে থাকতে পারে। আমি যখন আমার ফর্ম্যাটরগুলি এভাবে প্রয়োগ করি তখন আমার একটি সমস্যা হয়েছিল:

class Thing1Formatter {
  private static Thing2Formatter thing2Formatter = new Thing2Formatter();
  public format(Thing thing) {
      thing2Formatter.format(thing.innerThing2);
  }
}
class Thing2Formatter {
  private static Thing1Formatter thing1Formatter = new Thing1Formatter();
  public format(Thing2 thing) {
      thing1Formatter.format(thing.innerThing1);
  }
}

হ্যাঁ, আমি জানি হ্যাশম্যাপ এবং আরও কিছু কোডও এটি ঠিক করতে পারে। তবে "উদাহরণস্বরূপ" তুলনা করে এতটাই পঠনযোগ্য এবং বজায় রাখা যায় বলে মনে হয়। সরল কিন্তু দুর্গন্ধযুক্ত কিছু আছে কি?

নোট 5-10/2010 যোগ করা হয়েছে:

দেখা যাচ্ছে যে ভবিষ্যতে নতুন সাবক্ল্যাসগুলি সম্ভবত যুক্ত করা হবে এবং আমার বিদ্যমান কোডগুলিকে এগুলি নিখুঁতভাবে পরিচালনা করতে হবে। ক্লাসে হ্যাশম্যাপ কাজ করবে না কারণ ক্লাসটি খুঁজে পাওয়া যাবে না। যদি সুনির্দিষ্ট থেকে শুরু করে সর্বাধিক সাধারণের সাথে শেষ হয় তবে বিবৃতিগুলির একটি শৃঙ্খলা সম্ভবত সর্বোত্তম:

if (obj instanceof SubClass1) {
    // Handle all the methods and properties of SubClass1
} else if (obj instanceof SubClass2) {
    // Handle all the methods and properties of SubClass2
} else if (obj instanceof Interface3) {
    // Unknown class but it implements Interface3
    // so handle those methods and properties
} else if (obj instanceof Interface4) {
    // likewise.  May want to also handle case of
    // object that implements both interfaces.
} else {
    // New (unknown) subclass; do what I can with the base class
}

4
আমি একটি [দর্শকের ধরণ] [1] পরামর্শ দেব। [1]: en.wikipedia.org/wiki/Visitor_pattern
lexicore

25
ভিজিটর প্যাটার্নের জন্য লক্ষ্য শ্রেণিতে একটি পদ্ধতি যুক্ত করা প্রয়োজন (উদাহরণস্বরূপ পূর্ণসংখ্যা) - জাভাস্ক্রিপ্টে সহজ, জাভাতে শক্ত। টার্গেট ক্লাস ডিজাইন করার সময় দুর্দান্ত প্যাটার্ন; কোনও পুরানো ক্লাসকে নতুন কৌশল শেখানোর চেষ্টা করার সময় এত সহজ নয়।
মার্ক লুটন

4
@ ফ্লেক্সিকোর: মন্তব্যে মার্কডাউন সীমিত। [text](link)মন্তব্যগুলিতে লিঙ্ক পোস্ট করতে ব্যবহার করুন ।
বালাসসি

2
"তবে জাভা ঠিক সেভাবে কাজ করে না।" হতে পারে আমি জিনিসগুলি ভুল বুঝি, তবে জাভা পদ্ধতি ওভারলোডিংকে সমর্থন করে (এমনকি স্থির পদ্ধতিতেও) ঠিক আছে ... এটি ঠিক যে আপনার উপরের পদ্ধতিগুলি রিটার্নের ধরণটি হারিয়েছে।
পাওয়ারলর্ড

4
@Powerlord ওভারলোড রেজল্যুশন হল স্ট্যাটিককম্পাইল-টাইম
আলেকজান্ডার ডাবিনস্কি

উত্তর:


55

আপনি স্টিভ ইয়েজের অ্যামাজন ব্লগ থেকে এই এন্ট্রিতে আগ্রহী হতে পারেন: "যখন পলিমারফিজম ব্যর্থ হয়" । মূলত তিনি এরকম কেসগুলিতে সম্বোধন করছেন, যখন পলিমারফিজম এর সমাধান হওয়ার চেয়ে বেশি সমস্যার সৃষ্টি করে।

বিষয়টি হ'ল পলিমারফিজমটি ব্যবহার করতে আপনাকে এই ক্ষেত্রে প্রতিটি 'স্যুইচিং' শ্রেণীর "হ্যান্ডেল" অংশটি যুক্ত করতে হবে - অর্থাত্ পূর্ণসংখ্যা ইত্যাদি। স্পষ্টতই এটি ব্যবহারিক নয়। কখনও কখনও এটি যুক্তিযুক্তভাবে কোড রাখার সঠিক জায়গাও হয় না। তিনি 'উদাহরণস্বরূপ' পদ্ধতির প্রস্তাব দিয়েছেন যাতে বেশ কয়েকটি কুফল কম হয়।

সমস্ত ক্ষেত্রে যেমন আপনি দুর্গন্ধযুক্ত কোড লিখতে বাধ্য হন, তেমন একটি পদ্ধতিতে (বা সর্বাধিক এক শ্রেণিতে) বোতামটি রাখুন যাতে গন্ধ বের হয় না।


22
পলিমারফিজম ব্যর্থ হয় না। বরং, স্টিভ ইয়েজি ভিজিটর প্যাটার্নটি আবিষ্কার করতে ব্যর্থ হয়েছে, এটির জন্য উপযুক্ত প্রতিস্থাপন instanceof
রোটসর

12
আমি এখানে দেখি না যে দর্শক এখানে কীভাবে সহায়তা করে। মুল বক্তব্যটি হ'ল ওপিনিওনেটেডএলএফ-এর প্রতিক্রিয়া নিউমন্সটারে নিউমনস্টারে এনকোড করা উচিত নয়, তবে OpinionatedElf এ থাকা উচিত।
ডিজে ক্লেওয়ার্থ

2
উদাহরণের বিষয়টি হ'ল OpinionatedElf উপলব্ধ ডেটা থেকে জানাতে পারে না যে এটি মনস্টারটিকে পছন্দ করে বা অপছন্দ করে। এটি দানব কোন শ্রেণীর অন্তর্গত তা জানতে হবে। এর জন্য হয় একটি উদাহরণ প্রয়োজন, বা দ্যস্টারটি কোনওভাবে জানতে হবে যে OpinionatedElf এটি পছন্দ করে। দর্শনার্থী যে গোল হয় না।
ডিজেক্লেওয়ার্থ

2
@ ডিজেক্লেওয়ার্থ ভিজিটর ধাঁচটি ক্লাসে একটি পদ্ধতি যুক্ত করে এটির কাছাকাছি পৌঁছেছে Monster, যার দায়িত্ব মূলত "হ্যালো, আমি একটি অর্কি। আপনি আমার সম্পর্কে কী ভাবেন?" এর মতো অবজেক্টটি পরিচয় করিয়ে দেওয়া। মতামতযুক্ত এলফ এর অনুরূপ কোড সহ এই "শুভেচ্ছা" এর উপর ভিত্তি করে দানবদের বিচার করতে পারে bool visitOrc(Orc orc) { return orc.stench()<threshold; } bool visitFlower(Flower flower) { return flower.colour==magenta; }। একমাত্র দানব-নির্দিষ্ট কোডটি তখন class Orc { <T> T accept(MonsterVisitor<T> v) { v.visitOrc(this); } }প্রতিটি দানব পরিদর্শন একবারে এবং সকলের জন্য যথেষ্ট।
রোটসর

2
কিছু কিছু ক্ষেত্রে কেন ভিজিটর আবেদন করা যাবে না তার জন্য @ ক্রিস নাইটের উত্তর দেখুন।
জেমস পি।

20

মন্তব্যে হাইলাইট হিসাবে, দর্শনার্থীর প্যাটার্নটি একটি ভাল পছন্দ হবে। তবে লক্ষ্য / গ্রহণকারী / ভিসিটির উপর সরাসরি নিয়ন্ত্রণ ছাড়াই আপনি সেই ধরণটি প্রয়োগ করতে পারবেন না। এখানে র্যাপারগুলি ব্যবহার করে উপক্লাশগুলির উপর আপনার সরাসরি নিয়ন্ত্রণ না থাকলেও (উদাহরণ হিসাবে পূর্ণসংখ্যাকে গ্রহণ করে) এখানে দর্শনার্থীদের প্যাটার্নটি সম্ভবত ব্যবহার করা যেতে পারে:

public class IntegerWrapper {
    private Integer integer;
    public IntegerWrapper(Integer anInteger){
        integer = anInteger;
    }
    //Access the integer directly such as
    public Integer getInteger() { return integer; }
    //or method passthrough...
    public int intValue() { return integer.intValue(); }
    //then implement your visitor:
    public void accept(NumericVisitor visitor) {
        visitor.visit(this);
    }
}

অবশ্যই, একটি চূড়ান্ত শ্রেণি মোড়ানো এটির নিজস্ব গন্ধ হিসাবে বিবেচিত হতে পারে তবে এটি আপনার সাবক্লাসগুলির সাথে খুব ভাল। ব্যক্তিগতভাবে, আমি মনে করি না যে instanceofএটি এখানে দুর্গন্ধযুক্ত, বিশেষত এটি যদি কোনও পদ্ধতিতে সীমাবদ্ধ থাকে এবং আমি খুশিতে এটি ব্যবহার করতে পারি (সম্ভবত আমার নিজের পরামর্শের উপরে)। আপনি যেমনটি বলেছেন, এটি বেশ পঠনযোগ্য, টাইপসেফ এবং রক্ষণাবেক্ষণযোগ্য। সর্বদা হিসাবে, এটি সহজ রাখুন।


হ্যাঁ, "ফর্ম্যাটটার", "সংমিশ্রণ", "বিভিন্ন ধরণের" সমস্ত দর্শনার্থীর দিক নির্দেশ করতে ams
টমাস আহলে

3
আপনি কীভাবে আবরণ ব্যবহার করবেন তা কীভাবে নির্ধারণ করবেন? উদাহরণস্বরূপ শাখার মাধ্যমে?
দ্রুত দাঁত

2
@ ব্রেকফাস্ট টুথ যেমন এই সমাধানটি দেখায় কেবল সমস্যাটি সরিয়ে দেয়। instanceofসঠিক handle()পদ্ধতিতে কল করার পরিবর্তে এখন আপনি এটি কলটি সঠিক XWrapperনির্মাণকারীর ব্যবহার করতে হবে ...
ম্যাথিয়াস

15

বিশাল আকারের পরিবর্তে if, আপনি যে মানচিত্রগুলি পরিচালনা করেন তা কোনও মানচিত্রে রাখতে পারেন (কী: শ্রেণি, মান: হ্যান্ডলার)।

কীটি কীভাবে ফিরে আসে তা যদি অনুসন্ধান করে তবে nullএকটি বিশেষ হ্যান্ডলার পদ্ধতি কল করুন যা কোনও ম্যাচিং হ্যান্ডলারের সন্ধান করার চেষ্টা করে (উদাহরণস্বরূপ isInstance()মানচিত্রে প্রতিটি কীটিতে কল করে)।

কোনও হ্যান্ডলার পাওয়া গেলে, এটি নতুন কী এর অধীনে নিবন্ধন করুন।

এটি সাধারণ কেসটিকে দ্রুত এবং সহজ করে তোলে এবং আপনাকে উত্তরাধিকার পরিচালনা করতে দেয়।


+1 এক্সএমএল স্কিমা বা মেসেজিং সিস্টেম থেকে উৎপন্ন কোড পরিচালনা করার সময় আমি এই পদ্ধতির ব্যবহার করেছি, যেখানে কয়েক ডজন অবজেক্টের ধরণ রয়েছে যা মূলত নন-টাইপসেফ পদ্ধতিতে আমার কোডে দেওয়া হয়েছে।
ডিএনএ

13

আপনি প্রতিবিম্ব ব্যবহার করতে পারেন:

public final class Handler {
  public static void handle(Object o) {
    try {
      Method handler = Handler.class.getMethod("handle", o.getClass());
      handler.invoke(null, o);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  public static void handle(Integer num) { /* ... */ }
  public static void handle(BigDecimal num) { /* ... */ }
  // to handle new types, just add more handle methods...
}

আপনি নির্দিষ্ট ইন্টারফেস প্রয়োগ করে সাবক্লাস এবং ক্লাসগুলি সাধারণভাবে পরিচালনা করতে ধারণাটি প্রসারিত করতে পারেন।


34
আমি যুক্তি দিয়ে বলব যে এটি অপারেটর অপারেটরের পরে আরও গন্ধ পাচ্ছে। যদিও কাজ করা উচিত।
টিম বাথ

5
@ টিম বাথ: if then elseহ্যান্ডলারদের যোগ, অপসারণ বা সংশোধন করার জন্য কমপক্ষে আপনার কোনও ক্রমবর্ধমান চেইনের মোকাবেলা করতে হবে না । কোড পরিবর্তন করতে কম ভঙ্গুর হয়। সুতরাং আমি বলব যে এই কারণে এটি instanceofপদ্ধতির চেয়ে উচ্চতর । যাইহোক, আমি কেবল একটি বৈধ বিকল্প দিতে চেয়েছিলাম।
জর্দো

1
এটি একটি গতিশীল ভাষা কীভাবে হাঁসের টাইপিংয়ের
ডিএনএ

@DNA: যে হবে না multimethods ?
জর্দো

1
আপনি কেন ব্যবহার না করে সমস্ত পদ্ধতিতে পুনরাবৃত্তি করবেন getMethod(String name, Class<?>... parameterTypes)? অন্যথায় আমি প্রতিস্থাপন করবে ==সঙ্গে isAssignableFromপ্যারামিটার ধরন চেক জন্য।
আলেকসান্দ্র ডাবিনস্কি

9

আপনি চেইন অফ দায়িত্বশীলতার ধরণটি বিবেচনা করতে পারেন । আপনার প্রথম উদাহরণের জন্য, এরকম কিছু:

public abstract class StuffHandler {
   private StuffHandler next;

   public final boolean handle(Object o) {
      boolean handled = doHandle(o);
      if (handled) { return true; }
      else if (next == null) { return false; }
      else { return next.handle(o); }
   }

   public void setNext(StuffHandler next) { this.next = next; }

   protected abstract boolean doHandle(Object o);
}

public class IntegerHandler extends StuffHandler {
   @Override
   protected boolean doHandle(Object o) {
      if (!o instanceof Integer) {
         return false;
      }
      NumberHandler.handle((Integer) o);
      return true;
   }
}

এবং তারপরে একইভাবে আপনার অন্যান্য হ্যান্ডলারের জন্য। তারপরে এটি স্টাফহ্যান্ডারদের একসাথে স্ট্রিংয়ের একটি ক্ষেত্রে (সুনির্দিষ্ট নির্দিষ্ট থেকে সুনির্দিষ্ট, একটি চূড়ান্ত 'ফ্যালব্যাক' হ্যান্ডলার সহ) এবং আপনার প্রেরণকারী কোডটি ঠিক firstHandler.handle(o);

(একটি বিকল্প হল চেইন ব্যবহার না করে কেবল List<StuffHandler>আপনার প্রেরণকারী ক্লাসে একটি রাখুন এবং handle()সত্যটি প্রত্যাবর্তনের আগ পর্যন্ত তালিকার মধ্যে এটি লুপ করুন )।


9

আমি মনে করি যে সর্বোত্তম সমাধান হ্যাশম্যাপ হ'ল কী হিসাবে ক্লাস এবং মান হিসাবে হ্যান্ডলার। মনে রাখবেন যে হ্যাশম্যাপ ভিত্তিক দ্রবণটি ধ্রুবক অ্যালগোরিদমিক জটিলতা runs (1) এ চলতে থাকে, যখন if-ਇੰਸਟসফ-অন্যের গন্ধ শৃঙ্খলাটি লিনিয়ার অ্যালগোরিদমিক জটিলতা O (N) তে সঞ্চালিত হয়, যেখানে N যদি if-উদাহরণ-অন্য শৃঙ্খলে লিঙ্কের সংখ্যা of (অর্থাত্ পরিচালিত বিভিন্ন শ্রেণীর সংখ্যা)। সুতরাং হাশম্যাপ ভিত্তিক সমাধানের কার্যকারিতা যদি উদাহরণস্বরূপ-অন্য চেইন সমাধানের কার্যকারিতার চেয়ে asyptotically উচ্চ N গুণ বেশি হয়। বিবেচনা করুন যে আপনাকে বার্তা শ্রেণীর বিভিন্ন বংশধরকে আলাদাভাবে পরিচালনা করতে হবে: বার্তা 1, বার্তা 2 ইত্যাদি। নীচে হাশম্যাপ ভিত্তিক হ্যান্ডলিংয়ের কোড স্নিপেট রয়েছে।

public class YourClass {
    private class Handler {
        public void go(Message message) {
            // the default implementation just notifies that it doesn't handle the message
            System.out.println(
                "Possibly due to a typo, empty handler is set to handle message of type %s : %s",
                message.getClass().toString(), message.toString());
        }
    }
    private Map<Class<? extends Message>, Handler> messageHandling = 
        new HashMap<Class<? extends Message>, Handler>();

    // Constructor of your class is a place to initialize the message handling mechanism    
    public YourClass() {
        messageHandling.put(Message1.class, new Handler() { public void go(Message message) {
            //TODO: IMPLEMENT HERE SOMETHING APPROPRIATE FOR Message1
        } });
        messageHandling.put(Message2.class, new Handler() { public void go(Message message) {
            //TODO: IMPLEMENT HERE SOMETHING APPROPRIATE FOR Message2
        } });
        // etc. for Message3, etc.
    }

    // The method in which you receive a variable of base class Message, but you need to
    //   handle it in accordance to of what derived type that instance is
    public handleMessage(Message message) {
        Handler handler = messageHandling.get(message.getClass());
        if (handler == null) {
            System.out.println(
                "Don't know how to handle message of type %s : %s",
                message.getClass().toString(), message.toString());
        } else {
            handler.go(message);
        }
    }
}

জাভাতে শ্রেণীর ধরণের শ্রেণীর ব্যবহারের বিষয়ে আরও তথ্য: http://docs.oracle.com/javase/tutorial/reflect/class/classNew.html


অল্প সংখ্যক মামলার ক্ষেত্রে (কোনও বাস্তব উদাহরণের জন্য সম্ভবত এই শ্রেণীর সংখ্যার চেয়ে বেশি) যদি অন্যথায় মানচিত্রকে ছাড়িয়ে যায় তবে হিপ মেমরিটি ব্যবহার না করা ছাড়াও
ইডেলওয়াল ২


0

আমি এই সমস্যাটি ব্যবহার করে সমাধান করেছি reflection(জেনারিক্সের প্রাক যুগে প্রায় 15 বছর আগে)।

GenericClass object = (GenericClass) Class.forName(specificClassName).newInstance();

আমি একটি জেনেরিক ক্লাস (বিমূর্ত বেস ক্লাস) সংজ্ঞায়িত করেছি। বেস ক্লাসের অনেকগুলি কংক্রিট বাস্তবায়ন আমি সংজ্ঞায়িত করেছি। প্রতিটি কংক্রিট ক্লাস প্যারামিটার হিসাবে ক্লাস নাম দিয়ে লোড করা হবে। এই শ্রেণীর নামটি কনফিগারেশনের অংশ হিসাবে সংজ্ঞায়িত করা হয়েছে।

বেস ক্লাস সমস্ত কংক্রিট ক্লাস জুড়ে সাধারণ রাষ্ট্রকে সংজ্ঞায়িত করে এবং কংক্রিট ক্লাসগুলি বেস শ্রেণিতে সংজ্ঞায়িত বিমূর্ত বিধিগুলিকে ওভাররাইড করে রাষ্ট্রকে সংশোধন করবে।

এই সময়ে, আমি এই প্রক্রিয়াটির নাম জানি না, যা হিসাবে পরিচিত reflection

এই নিবন্ধে আরও কয়েকটি বিকল্প তালিকাভুক্ত করা হয়েছে : Mapএবং enumপ্রতিচ্ছবি ছাড়াও।


শুধু কৌতূহলী, আপনি GenericClassএকটি তৈরি করেন নি কেন interface?
Ztyx

আমার প্রচলিত রাষ্ট্র এবং ডিফল্ট আচরণ ছিল, যা অনেকগুলি সম্পর্কিত অবজেক্টে ভাগ করে নিতে হয়
রবীন্দ্র বাবু
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.