দীর্ঘ পরামিতি তালিকাগুলিযুক্ত কনস্ট্রাক্টর ব্যবহার না করেই বড়, অপরিবর্তনীয় বস্তু তৈরি করা


96

আমার কাছে কিছু বড় (3 টিরও বেশি ক্ষেত্র) অবজেক্ট রয়েছে যা অপরিবর্তনীয় হতে পারে এবং হওয়া উচিত। প্রতিবার যখন আমি এই ক্ষেত্রে যাই তখন আমি দীর্ঘ পরামিতি তালিকাগুলি সহ কন্সট্রাক্টর জঘন্যতা তৈরি করার ঝোঁক করি।

এটি সঠিক বোধ করে না, এটি ব্যবহার করা শক্ত এবং পঠনযোগ্যতা ভোগ করে।

ক্ষেত্রগুলি তালিকার মতো সংগ্রহের ধরণের কিছু হলেও এটি আরও খারাপ। একটি সরল addSibling(S s)বস্তু তৈরিতে এতটা স্বাচ্ছন্দ্য দেয় তবে বস্তুকে পরিবর্তনীয় করে তোলে।

ছেলেরা এরকম ক্ষেত্রে কী ব্যবহার করবেন?

আমি স্কেলা এবং জাভাতে আছি, তবে আমি মনে করি সমস্যাটি ভাষা অজ্ঞাব্যবস্থার যতক্ষণ না ভাষা অবজেক্ট ভিত্তিক হয়।

সমাধানগুলি আমি ভাবতে পারি:

  1. "দীর্ঘ পরামিতি তালিকার সাথে নির্মাণকারী জঘন্যতা"
  2. বিল্ডার প্যাটার্ন

5
আমি মনে করি বিল্ডার প্যাটার্নই এর সেরা এবং সর্বাধিক মান সমাধান।
জ্যাকারি রাইট

@ জাচারি: আপনি যা পরামর্শ দিচ্ছেন তা কেবল বিল্ডার প্যাটার্নের একটি বিশেষ ফর্মের জন্য কাজ করে, যেমনটি জোশুয়া ব্লচের ব্যাখ্যা করেছেন: drdobbs.com/java/208403883?pgno=2 আমি ফোন করতে সম্পর্কিত "সাবলীল ইন্টারফেস" শব্দটির সাথে যেতে পছন্দ করি এই "বিল্ডার প্যাটার্নের বিশেষ ফর্ম" (আমার উত্তর দেখুন)।
সিনট্যাক্স T3rr0r

উত্তর:


76

ঠিক আছে, আপনি একবারে সহজেই পড়ার সহজ এবং অপরিবর্তনীয় অবজেক্ট দুটি তৈরি করতে চান?

আমি মনে করি একটি সাবলীল ইন্টারফেস সঠিকভাবে সম্পন্ন হয়েছে আপনাকে সাহায্য করবে।

এটি দেখতে দেখতে (নিখুঁতভাবে তৈরি উদাহরণ):

final Foo immutable = FooFactory.create()
    .whereRangeConstraintsAre(100,300)
    .withColor(Color.BLUE)
    .withArea(234)
    .withInterspacing(12)
    .build();

আমি সাহসী "CONRECTLY DONE" লিখেছি কারণ বেশিরভাগ জাভা প্রোগ্রামাররা সাবলীল ইন্টারফেসগুলি ভুল পেয়ে যায় এবং অবজেক্টটি তৈরির জন্য প্রয়োজনীয় পদ্ধতিতে তাদের বস্তুকে দূষিত করে দেয় যা অবশ্যই সম্পূর্ণ ভুল।

কৌশলটি হ'ল কেবল বিল্ড () পদ্ধতিটি আসলে একটি ফু তৈরি করে (অতএব আপনি ফু অপরিবর্তনীয় হতে পারেন)।

FooFactory.create () , যেখানে এক্সএক্সএক্স (..) এবং এক্সএক্সএক্সএক্স (..) সকলেই "অন্য কিছু" তৈরি করে।

অন্য কিছু হতে পারে এটি একটি ফুফ্যাক্টরি, এটি করার একটি উপায় এখানে ...

আপনি ফুফ্যাক্টরিটি দেখতে এমন হবে:

// Notice the private FooFactory constructor
private FooFactory() {
}

public static FooFactory create() {
    return new FooFactory();
}

public FooFactory withColor( final Color col ) {
    this.color = color;
    return this;
}

public Foo build() {
    return new FooImpl( color, and, all, the, other, parameters, go, here );
}

11
@ all: দয়া করে "FooImpl" এ "ইমপ্ল" পোস্টফিক্স সম্পর্কে কোনও অভিযোগ করবেন না: এই শ্রেণিটি একটি কারখানার অভ্যন্তরে লুকানো রয়েছে এবং সাবলীল ইন্টারফেস লেখার ব্যক্তি ছাড়া এটি আর কেউ দেখতে পাবে না। সমস্ত ব্যবহারকারীরই যত্নশীল হ'ল তিনি একটি "ফু" পান। আমি "FooImpl" "FooPointlessNitpick" হিসাবেও ডাকতে পারতাম;)
সিনট্যাক্স T3rr0r

5
অনুভূতিপূর্ব মনে হচ্ছে? ;) আপনার সম্পর্কে অতীতে নিটপিক করা হয়েছে। :)
গ্রেগ ডি

4
আমি বিশ্বাস করি সাধারণ ত্রুটি তিনি উল্লেখ যে লোকেদের "withXXX" (ইত্যাদি) পদ্ধতি যোগ করারFoo , বস্তুর বরং একটি পৃথক থাকার চেয়ে FooFactory
ডিন হার্ডিং

4
আপনার এখনও FooImpl8 টি পরামিতি সহ কনস্ট্রাক্টর রয়েছে। উন্নতি কী?
মোট

4
আমি এই কোডটি কল করব না immutableএবং আমি কারখানার আইটেমগুলিকে পুনরায় ব্যবহার করার ভয় পাব কারণ তারা মনে করে যে এটি। মানে: FooFactory people = FooFactory.create().withType("person"); Foo women = people.withGender("female").build(); Foo talls = people.tallerThan("180m").build();যেখানে tallsএখন কেবলমাত্র মহিলা থাকবে contain অপরিবর্তনীয় এপিআই দিয়ে এটি হওয়া উচিত নয়।
থমাস আহলে

60

স্কেলা ২.৮ এ, আপনি copyকেস ক্লাসে নামযুক্ত এবং ডিফল্ট প্যারামিটার পাশাপাশি পদ্ধতি ব্যবহার করতে পারেন । এখানে কয়েকটি উদাহরণ কোড রয়েছে:

case class Person(name: String, age: Int, children: List[Person] = List()) {
  def addChild(p: Person) = copy(children = p :: this.children)
}

val parent = Person(name = "Bob", age = 55)
  .addChild(Person("Lisa", 23))
  .addChild(Person("Peter", 16))

31
স্কাল ভাষা উদ্ভাবনের জন্য +1। হ্যাঁ, এটি খ্যাতি সিস্টেমের একটি অপব্যবহার তবে ... ওও ... আমি স্কালাকে এতই ভালবাসি যে আমাকে এটি করতে হয়েছিল। :)
মাল্যাক্স

4
ওহ, মানুষ ... আমি প্রায় সাদৃশ্য কিছু উত্তর দিয়েছেন! ভাল, আমি ভাল সংস্থায় আছি :-) আমি অবাক হয়েছি যে এর আগে আমি আপনার উত্তরটি দেখিনি, যদিও ... <শ্রাগ>
ড্যানিয়েল সি সোব্রাল

20

ঠিক আছে, এটিকে স্কেলা ২.৮ এ বিবেচনা করুন:

case class Person(name: String, 
                  married: Boolean = false, 
                  espouse: Option[String] = None, 
                  children: Set[String] = Set.empty) {
  def marriedTo(whom: String) = this.copy(married = true, espouse = Some(whom))
  def addChild(whom: String) = this.copy(children = children + whom)
}

scala> Person("Joseph").marriedTo("Mary").addChild("Jesus")
res1: Person = Person(Joseph,true,Some(Mary),Set(Jesus))

এটি অবশ্যই সমস্যাগুলির অংশীদারি করে। উদাহরণস্বরূপ, উপার্জন চেষ্টা espouseএবং Option[Person], এবং তারপর দুইজনের একে অপরের সাথে বিবাহ বন্ধনে আবদ্ধ হন পেয়ে। আমি private varএবং / অথবা কোনও privateকনস্ট্রাক্টর প্লাস কারখানায় অবলম্বন না করে সমাধানের কোনও উপায়ের কথা ভাবতে পারি না ।


11

এখানে আরও কয়েকটি বিকল্প রয়েছে:

বিকল্প 1

বাস্তবায়নটিকে নিজেই পরিবর্তনযোগ্য করে তুলুন, তবে ইন্টারফেসগুলি পৃথক করুন যা এটি পরিবর্তনযোগ্য এবং পরিবর্তনীয় হিসাবে প্রকাশ করে। এটি সুইং লাইব্রেরি ডিজাইন থেকে নেওয়া হয়েছে।

public interface Foo {
  X getX();
  Y getY();
}

public interface MutableFoo extends Foo {
  void setX(X x);
  void setY(Y y);
}

public class FooImpl implements MutableFoo {...}

public SomeClassThatUsesFoo {
  public Foo makeFoo(...) {
    MutableFoo ret = new MutableFoo...
    ret.setX(...);
    ret.setY(...);
    return ret; // As Foo, not MutableFoo
  }
}

বিকল্প 2

যদি আপনার অ্যাপ্লিকেশনটিতে অপরিবর্তনীয় অবজেক্টগুলির (যেমন, কনফিগারেশন অবজেক্টস) একটি বৃহত তবে প্রাক-সংজ্ঞায়িত সেট থাকে তবে আপনি বসন্তের ফ্রেমওয়ার্কটি ব্যবহার করে বিবেচনা করতে পারেন ।


4
বিকল্প 1 চালাক (তবে খুব চালাক নয়), তাই আমি এটি পছন্দ করি।
টিমোথি

4
আমি এটি আগে করেছি, তবে আমার মতে এটি ভাল সমাধান হওয়া থেকে অনেক দূরে কারণ বস্তুটি এখনও পরিবর্তনযোগ্য, কেবলমাত্র পরিবর্তনের পদ্ধতিগুলি "লুকানো"। সম্ভবত আমি এই বিষয় সম্পর্কে খুব বাছাই করছি ...
মাল্যাক্স

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

6

এটি বিভিন্ন ধরণের অপরিবর্তনীয়তা মনে রাখতে সহায়তা করে । আপনার ক্ষেত্রে, আমি মনে করি "পপসিকল" অপরিবর্তনশীলতা সত্যিই ভালভাবে কাজ করবে:

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

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


4
ডাউনভোটস? এটি কেন ভাল সমাধান নয় তা নিয়ে যে কেউ মন্তব্য করতে চান?
জুলিয়েট

+1 হতে পারে যে কেউ এটিকে হ্রাস করেছেন কারণ আপনি clone()নতুন দৃষ্টান্তগুলি সন্ধানের জন্য ইঙ্গিত দিচ্ছেন ।
ফাইন

বা সম্ভবত এটি ছিল যে এটি জাভা থ্রেড সুরক্ষার সাথে আপস করতে পারে: java.sun.com/docs/books/jls/third_edition/html/memory.html#17.5
ফিনিউ

একটি অসুবিধা হ'ল ভবিষ্যতের বিকাশকারীদের "হিমায়িত" পতাকাটি হ্যান্ডল করার ক্ষেত্রে সতর্ক হওয়া দরকার। যদি কোনও মিউটর পদ্ধতি পরে যুক্ত করা হয় তবে পদ্ধতিটি হিমায়িত নয় তা জোর করে ভুলে যায়, সমস্যা হতে পারে। একইভাবে, যদি কোনও নতুন কনস্ট্রাক্টর লেখা থাকে যা উচিত, তবে না, freeze()পদ্ধতিটি কল করে , জিনিসগুলি কুৎসিত হতে পারে।
stale ব্যাখ্যাzel

5

আপনি অপরিবর্তনীয় বস্তুগুলি এমন পদ্ধতিগুলি উদ্ভাসিত করতে পারেন যা মিউটরগুলির মতো দেখতে (অ্যাডসিবলিংয়ের মতো) তবে তাদের একটি নতুন উদাহরণ ফেরত দেওয়া হোক। অপরিবর্তনীয় স্কালা সংগ্রহগুলি এটাই করে।

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

উদাহরণস্বরূপ একটি গ্রাফ প্রান্ত যার গন্তব্য নেই এখনও কোনও বৈধ গ্রাফ প্রান্ত নয়।


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

4
@ গুস্তাফ্যাক: হ্যাঁ ক্লিফ ক্লিক একবার তাদের গল্পটি বলেছিল যে তারা কীভাবে রিচ হিকির ক্লোজার অ্যান্ট কলোনির সিমুলেশনকে তাদের বড় আকারের একটিতে (864 কোর, 768 গিগাবাইট র‌্যাম) সিমুলেশন করেছে: 700 সমান্তরাল থ্রেড 700 কোরে চলমান, প্রতিটিই 100%, 20 গিগাবাইটেরও বেশি উত্পন্ন করে ক্ষণজীবী আবর্জনা প্রতি সেকেন্ডে । জিসি এমনকি একটি ঘামও ভাঙ্গেনি।
জার্গ ডব্লু মিত্তাগ

5

চারটি সম্ভাবনা বিবেচনা করুন:

new Immutable(one, fish, two, fish, red, fish, blue, fish); /*1 */

params = new ImmutableParameters(); /*2 */
params.setType("fowl");
new Immutable(params);

factory = new ImmutableFactory(); /*3 */
factory.setType("fish");
factory.getInstance();

Immutable boringImmutable = new Immutable(); /* 4 */
Immutable lessBoring = boringImmutable.setType("vegetable");

আমার কাছে, 2, 3 এবং 4 এর প্রত্যেকেই একটি পার্থক্যের অবস্থার সাথে মানিয়ে নিয়েছে। প্রথমটি পছন্দ করা শক্ত, কারণ ওপি দ্বারা উদ্ধৃত কারণগুলির জন্য, এবং সাধারণত এটি এমন একটি নকশার লক্ষণ যা কিছু ক্রিপ ভোগ করেছে এবং তার জন্য কিছু রিফ্যাক্টরিং প্রয়োজন।

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

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

মনে রাখবেন যে আমি 'প্যাটার্ন ফ্যান ক্লাব'-এর সদস্য নই - অবশ্যই, কিছু জিনিস অনুকরণ করার মতো, তবে আমার কাছে মনে হয় যে তারা যখন তাদের নাম এবং মজার টুপি দেয় তখন তারা নিজেরাই একটি অস্বাস্থ্যকর জীবনযাপন করে।


6
এটি বিল্ডার প্যাটার্ন (বিকল্প 2)
সাইমন নিকারসন

এটি কি কোনও কারখানা ('বিল্ডার') অবজেক্ট হবে না যা তার অপরিবর্তনীয় বস্তুগুলি নির্গত করে?
bmargulies

পার্থক্যটি বেশ সিনমেটিক বলে মনে হচ্ছে। শিমটি কীভাবে তৈরি হয়? কীভাবে এটি কোনও বিল্ডারের থেকে আলাদা?
কার্ল

আপনি কোনও নির্মাতার (বা অন্য অনেক কিছুর জন্য) জাভা বিন কনভেনশনগুলির প্যাকেজ চান না।
টম হাটিন - 13

4

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


4
দ্রষ্টব্য: এই উত্তরের জন্য মাইলেজ সমস্যা, কোড-বেস এবং বিকাশকারী দক্ষতার সাথে পৃথক হতে পারে।
কার্ল

2

আমি সি # ব্যবহার করি এবং এটি আমার পন্থা। বিবেচনা:

class Foo
{
    // private fields only to be written inside a constructor
    private readonly int i;
    private readonly string s;
    private readonly Bar b;

    // public getter properties
    public int I { get { return i; } }
    // etc.
}

অপশন 1. option চ্ছিক পরামিতি সহ নির্মাতা

public Foo(int i = 0, string s = "bla", Bar b = null)
{
    this.i = i;
    this.s = s;
    this.b = b;
}

যেমন ব্যবহার করা হয় new Foo(5, b: new Bar(whatever))। জাভা বা সি # সংস্করণের জন্য 4.0 এর আগে নয়। তবে এখনও দেখানো মূল্যবান, কারণ এটি কীভাবে সমস্ত সমাধান ভাষা অজ্ঞেয় সম্পর্কিত নয়।

বিকল্প ২. একক প্যারামিটার অবজেক্ট গ্রহণকারী কনস্ট্রাক্টর

public Foo(FooParameters parameters)
{
    this.i = parameters.I;
    // etc.
}

class FooParameters
{
    // public properties with automatically generated private backing fields
    public int I { get; set; }
    public string S { get; set; }
    public Bar B { get; set; }

    // All properties are public, so we don't need a full constructor.
    // For convenience, you could include some commonly used initialization
    // patterns as additional constructors.
    public FooParameters() { }
}

ব্যবহারের উদাহরণ:

FooParameters fp = new FooParameters();
fp.I = 5;
fp.S = "bla";
fp.B = new Bar();
Foo f = new Foo(fp);`

On.০ থেকে সি # এটি অবজেক্ট ইনিশিয়ালাইজার সিনট্যাক্সের সাথে আরও সুন্দর করে তোলে (পূর্ববর্তী উদাহরণের সাথে শব্দার্থগতভাবে সমান):

FooParameters fp = new FooParameters { I = 5, S = "bla", B = new Bar() };
Foo f = new Foo(fp);

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

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