টন প্যারামিটার বনাম বিল্ডার প্যাটার্ন সহ নির্মাণকারী


21

এটি ভালভাবে জানে যে আপনার ক্লাসে যদি অনেক পরামিতি সহ কোনও কনস্ট্রাক্টর থাকে তবে 4 এর বেশি বলুন, তবে এটি সম্ভবত একটি কোডের গন্ধ । ক্লাসটি এসআরপিকে সন্তুষ্ট করলে আপনাকে পুনর্বিবেচনা করা দরকার ।

তবে যদি আমরা 10 বা ততোধিক পরামিতিগুলির উপর নির্ভর করে যা তৈরি এবং অবজেক্ট করি এবং শেষ পর্যন্ত বিল্ডার প্যাটার্নের মাধ্যমে সমস্ত পরামিতিগুলি সেট করে শেষ করি? কল্পনা করুন, আপনি Personএর ব্যক্তিগত তথ্য, কাজের তথ্য, বন্ধুদের তথ্য, আগ্রহের তথ্য, শিক্ষার তথ্য এবং আরও কিছু দিয়ে একটি প্রকারের অবজেক্ট তৈরি করেন। এটি ইতিমধ্যে ভাল, তবে আপনি কোনওভাবে 4 টিরও বেশি পরামিতি সেট করেছেন, তাই না? কেন এই দুটি মামলা এক হিসাবে বিবেচিত হয় না?

উত্তর:


25

বিল্ডার প্যাটার্নটি অনেক যুক্তিগুলির "সমস্যা" সমাধান করে না। তবে কেন অনেক যুক্তি সমস্যাযুক্ত?

  • তারা আপনাকে বোঝায় যে আপনার ক্লাসটি খুব বেশি করছে । তবে, এমন অনেক ধরণের রয়েছে যা বৈধভাবে অনেক সদস্যকে ধারণ করে যেগুলি সংজ্ঞাগতভাবে গোষ্ঠীভুক্ত করা যায় না।
  • অনেক ইনপুট দিয়ে একটি ফাংশন পরীক্ষা করা এবং বোঝা দ্রুততর জটিল হয় - আক্ষরিক!
  • ভাষা যখন নামযুক্ত পরামিতিগুলি সরবরাহ করে না, তখন একটি ফাংশন কল স্ব-ডকুমেন্টিং হয় না । অনেক যুক্তি সহ একটি ফাংশন কল পড়া বেশ কঠিন কারণ 7 তম প্যারামিটারটি করার কথা আপনার কোনও ধারণা নেই। এমনকি আপনি 5 ম এবং argument ষ্ঠ যুক্তিটি দুর্ঘটনাক্রমে অদলবদল হয়েছে কিনা তা লক্ষ্য করবেন না, বিশেষত যদি আপনি গতিময় টাইপ করা ভাষায় থাকেন বা সমস্ত কিছু স্ট্রিং হয়ে থাকে বা যখন শেষ প্যারামিটার trueকোনও কারণে হয়।

নামকরণ পরামিতি নকল

নির্মাতা প্যাটার্ন ঠিকানাগুলি এই সমস্যার অনেক আর্গুমেন্ট সহ ফাংশন কল যথা maintainability উদ্বেগ মাত্র এক * । মত একটি ফাংশন কল

MyClass o = new MyClass(a, b, c, d, e, f, g);

হতে পারে

MyClass o = MyClass.builder()
  .a(a).b(b).c(c).d(d).e(e).f(f).g(g)
  .build();

Build বিল্ডার প্যাটার্নটি মূলত যৌগিক অবজেক্টগুলিকে একত্রিত করার জন্য একটি প্রতিনিধিত্ব-অজ্ঞানীয় পদ্ধতির হিসাবে লক্ষ্য করা হয়েছিল, যা কেবলমাত্র পরামিতিগুলির জন্য নাম দেওয়া যুক্তির চেয়ে অনেক বেশি উচ্চাকাঙ্ক্ষা। বিশেষত, বিল্ডার প্যাটার্নটির জন্য একটি সাবলীল ইন্টারফেসের প্রয়োজন হয় না।

এটি কিছুটা অতিরিক্ত সুরক্ষার প্রস্তাব দেয় কারণ আপনি যদি এমন কোনও বিল্ডার পদ্ধতি অবলম্বন করেন যা এটি উপস্থিত না হয় তবে এটি অন্যথায় আপনার কাছে এমন কিছু আনবে না যা কনস্ট্রাক্টর কলটিতে কোনও মন্তব্য না করে। এছাড়াও, ম্যানুয়ালি কোনও বিল্ডার তৈরি করতে কোডের প্রয়োজন হয় এবং আরও কোডটিতে সর্বদা আরও বাগ থাকতে পারে।

যে ভাষাগুলিতে একটি নতুন মান প্রকারের সংজ্ঞা দেওয়া সহজ, সেখানে আমি খুঁজে পেয়েছি যে নামযুক্ত যুক্তিগুলির অনুকরণের জন্য মাইক্রোটাইপিং / ক্ষুদ্র প্রকারগুলি ব্যবহার করা আরও ভাল । এটির নামকরণ করা হয়েছে কারণ প্রকারগুলি সত্যই ছোট, তবে আপনি আরও অনেকগুলি টাইপ করে শেষ করেন ;-)

MyClass o = new MyClass(
  new MyClass.A(a), new MyClass.B(b), new MyClass.C(c),
  new MyClass.D(d), new MyClass.E(e), new MyClass.F(f),
  new MyClass.G(g));

একথাও ঠিক যে, টাইপ নামের A, B, C, ... স্ব-দলিল নাম যে পরামিতি প্রায়ই একই নামের অর্থ চিত্রিত হতে হিসাবে আপনি প্যারামিটার পরিবর্তনশীল দিতে চাই উচিত নয়। বিল্ডার-ফর-নেমড-আর্গুমেন্ট আইডিয়ামের সাথে তুলনা করে প্রয়োজনীয় বাস্তবায়নটি অনেক সহজ এবং এর ফলে বাগগুলি থাকার সম্ভাবনা কম। উদাহরণস্বরূপ (জাভা-ইশ সিনট্যাক্স সহ):

class MyClass {
  ...
  public static class A {
    public final int value;
    public A(int a) { value = a; }
  }
  ...
}

সংকলক আপনাকে গ্যারান্টি দেয় যে সমস্ত আর্গুমেন্ট সরবরাহ করা হয়েছিল; কোনও বিল্ডারের সাথে আপনাকে ম্যানুয়ালি নিখোঁজ যুক্তিগুলির জন্য পরীক্ষা করতে হবে, বা হোস্ট ল্যাঙ্গুয়েজ টাইপ সিস্টেমে কোনও স্টেট মেশিনকে এনকোড করতে হবে - উভয়টিতেই সম্ভবত বাগ রয়েছে contain

নামযুক্ত আর্গুমেন্টগুলি অনুকরণ করার জন্য আরও একটি সাধারণ পন্থা রয়েছে: একটি একক বিমূর্ত প্যারামিটার অবজেক্ট যা সমস্ত ক্ষেত্র আরম্ভ করার জন্য একটি ইনলাইন বর্গ বাক্য গঠন ব্যবহার করে। জাভাতে:

MyClass o = new MyClass(new MyClass.Arguments(){{ argA = a; argB = b; argC = c; ... }});

class MyClass {
  ...
  public static abstract class Arguments {
    public int argA;
    public String ArgB;
    ...
  }
}

তবে ক্ষেত্রগুলি ভুলে যাওয়া সম্ভব এবং এটি একটি ভাষা-নির্দিষ্ট সমাধান (আমি জাভাস্ক্রিপ্ট, সি #, এবং সি ব্যবহার করে দেখেছি)।

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

সুতরাং "অনেক নামবিহীন পরামিতি কোড বজায় রাখা কোডকে কঠিন করে তোলে " এর সমাধানের জন্য অনেকগুলি পন্থা রয়েছে , অন্য সমস্যাগুলি রয়ে গেছে।

মূল সমস্যার কাছে পৌঁছাচ্ছি

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

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

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

Dependencies deps = new Dependencies(a, d);
...
MyClass o = deps.newMyClass(b, c, e, f, g);

class MyClass {
  MyClass(Dependencies deps, B b, C c, E e, F f, G g) {
    this.depA = deps.newDepA(b, c);
    this.depB = deps.newDepB(e, f);
    this.g = g;
  }
  ...
}

class Dependencies {
  private A a;
  private D d;
  public Dependencies(A a, D d) { this.a = a; this.d = d; }
  public DepA newDepA(B b, C c) { return new DepA(a, b, c); }
  public DepB newDepB(E e, F f) { return new DepB(d, e, f); }
  public MyClass newMyClass(B b, C c, E e, F f, G g) {
    return new MyClass(deps, b, c, e, f, g);
  }
}

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


আমি স্ব-ডকুমেন্টিং অংশের বিরুদ্ধে সত্যই তর্ক করব। আপনার যদি একটি শালীন আইডিই + শালীন মন্তব্য থাকে তবে বেশিরভাগ সময় নির্মাতা এবং পরামিতি সংজ্ঞাটি একটি কীস্ট্রোক থেকে দূরে থাকে।
জাভেরিইএইচ

অ্যান্ড্রয়েড স্টুডিও 3.0.০-তে, কনস্ট্রাক্টরের প্যারামিটারের নামটি কনস্ট্রাক্টর কলে পাস করার মান সংলগ্ন প্রদর্শিত হয়। যেমন: নতুন এ (অপারেন্ড 1: 34, অপারেন্ড 2: 56); অপারেন্ড 1 এবং অপরেনড 2 কনস্ট্রাক্টরের পরামিতিগুলির নাম। কোডটি আরও পঠনযোগ্য করার জন্য এগুলি আইডিই দ্বারা দেখানো হয়েছে। সুতরাং, প্যারামিটারটি কী তা খুঁজে পেতে সংজ্ঞাতে যাওয়ার দরকার নেই।
গারনেট

9

বিল্ডার প্যাটার্নটি আপনার জন্য কিছু সমাধান করে না এবং ডিজাইনের ব্যর্থতাও ঠিক করে না।

আপনার যদি কোনও শ্রেণি নির্মাণের জন্য 10 প্যারামিটারের প্রয়োজন হয় তবে এটি নির্মাণের জন্য কোনও বিল্ডার তৈরি করা হঠাৎ আপনার নকশাটিকে আরও ভাল করে তুলবে না। আপনার প্রশ্নে ক্লাসটি রিফ্যাক্টর করার বিকল্প বেছে নেওয়া উচিত।

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


1

প্রতিটি অংশ যেমন ব্যক্তিগত তথ্য, কাজের তথ্য (প্রতিটি কর্মসংস্থান "স্ট্যান্ড"), প্রতিটি বন্ধু, ইত্যাদি তাদের নিজস্ব বস্তুতে রূপান্তরিত হওয়া উচিত।

আপনি কীভাবে কোনও আপডেট কার্যকারিতা কার্যকর করবেন তা বিবেচনা করুন। ব্যবহারকারী একটি নতুন কাজের ইতিহাস যুক্ত করতে চায় । ব্যবহারকারী তথ্যের অন্য কোনও অংশ বা পূর্ববর্তী কাজের ইতিহাস পরিবর্তন করতে চায় না - কেবল তাদের একই রাখুন।

যখন প্রচুর তথ্য থাকে, তথ্যের জন্য এক সময় এক টুকরো তৈরি করা অনিবার্য। আপনি এই টুকরোগুলি যৌক্তিকভাবে গ্রুপ করতে পারেন - মানুষের অন্তর্নিহিততার উপর ভিত্তি করে (যা কোনও প্রোগ্রামারকে ব্যবহার করা সহজ করে) বা ব্যবহারের প্যাটার্নের ভিত্তিতে (তথ্যের টুকরা সাধারণত একই সাথে আপডেট করা হয়)।

বিল্ডার প্যাটার্ন হ'ল টাইপ করা আর্গুমেন্টের একটি তালিকা (অর্ডার করা বা আনর্ডারড) ক্যাপচার করার একটি উপায় যা পরে প্রকৃত নির্মাণকারীকে দেওয়া যেতে পারে (বা নির্মাতা বিল্ডারের বন্দী যুক্তিগুলি পড়তে পারে)।

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

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

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