আপনি কি খোলা-বন্ধ নীতিটির সুবিধাগুলি উপার্জন করেন?


12

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

নিম্নলিখিতটি একটি সাধারণ, প্রসারযোগ্য শ্রেণীর বিবেচনা করুন:

class PaycheckCalculator {
    // ...
    protected decimal GetOvertimeFactor() { return 2.0M; }
}

উদাহরণস্বরূপ, এখন বলুন যে OvertimeFactor1.5 এ পরিবর্তন হয়েছে। যেহেতু উপরের শ্রেণিটি প্রসারিত করার জন্য ডিজাইন করা হয়েছিল, তাই আমি সহজেই সাবক্লাস করে অন্যরকম ফিরে আসতে পারি OvertimeFactor

তবে ... ক্লাসটি বর্ধিতকরণ এবং ওসিপি মেনে চলার জন্য ডিজাইন করা সত্ত্বেও, আমি প্রশ্নে পদ্ধতিটি সাবক্লাসিং ও ওভাররাইড না করে আমার আইওসি পাত্রে আমার অবজেক্টগুলিকে রি-ওয়্যারিংয়ের চেয়ে প্রশ্নে একক পদ্ধতি পরিবর্তন করব।

ফলস্বরূপ আমি ওসিপি যা অর্জন করার চেষ্টা করে তার একটি অংশ লঙ্ঘন করেছি। মনে হচ্ছে আমি কেবল অলস হয়ে যাচ্ছি কারণ উপরেরটি কিছুটা সহজ। আমি কি ওসিপিকে ভুল বুঝছি? আমার কি আসলেই আলাদা কিছু করা উচিত? আপনি কি ওসিপির সুবিধাগুলি আলাদাভাবে উপার্জন করেন?

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

উত্তর:


10

আপনি যদি বেস ক্লাসটি সংশোধন করছেন তবে এটি আসলে বন্ধ নয় এটিই!

আপনি বিশ্বব্যাপী লাইব্রেরিটি প্রকাশ করেছেন এমন পরিস্থিতিটি ভেবে দেখুন। যদি আপনি যান এবং ওভারটাইম ফ্যাক্টরটি 1.5 তে পরিবর্তন করে আপনার বেস শ্রেণীর আচরণ পরিবর্তন করেন তবে ক্লাসটি বন্ধ ছিল বলে ধরে নিয়ে আপনার কোড ব্যবহার করা সমস্ত লোককে আপনি লঙ্ঘন করেছেন।

সত্যিই ক্লাসটি বন্ধ করে দেওয়া কিন্তু খোলার জন্য আপনি কোনও বিকল্প উত্স (সম্ভবত কনফিগার ফাইল) থেকে ওভারটাইম ফ্যাক্টরটি পুনরুদ্ধার করা উচিত বা ভার্চুয়াল পদ্ধতিটি প্রমাণিত করা উচিত যা ওভাররাইড করা যায়?

যদি শ্রেণিটি সত্যই বন্ধ হয়ে যায় তবে আপনার পরিবর্তনের পরে কোনও পরীক্ষার কেস ব্যর্থ হবে না (ধরে নিলে আপনার সমস্ত পরীক্ষার ক্ষেত্রে আপনার 100% কভারেজ রয়েছে) এবং আমি ধরে নেব যে পরীক্ষা করার একটি কেস রয়েছে GetOvertimeFactor() == 2.0M

ইঞ্জিনিয়ারকে ওভার করবেন না

তবে এই ওপেন-ক্লোজ নীতিকে যৌক্তিক উপসংহারে নিয়ে যাবেন না এবং শুরু থেকেই কনফিগারযোগ্য সবকিছুই (এটি ইঞ্জিনিয়ারিংয়ের ওপরে)। আপনার বর্তমানে প্রয়োজনীয় বিটগুলি কেবল সংজ্ঞায়িত করুন।

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


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

@ রোগারিও: এটি সত্য হতে পারে (১৯৮৮ সালে ফিরে)। তবে বর্তমান সংজ্ঞা (১৯৯০ সালে যখন ওও জনপ্রিয় হয়ে ওঠে জনপ্রিয়) এটি একটি সামঞ্জস্যপূর্ণ পাবলিক ইন্টারফেস বজায় রাখার বিষয়ে। During the 1990s, the open/closed principle became popularly redefined to refer to the use of abstracted interfaces, where the implementations can be changed and multiple implementations could be created and polymorphically substituted for each other. en.wikedia.org/wiki/Open/closed_pr सिद्धांत
মার্টিন ইয়র্ক

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

3

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

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


2

আপনি যে উদাহরণ পোস্ট করেছেন তাতে ওভারটাইম ফ্যাক্টরটি একটি পরিবর্তনশীল বা ধ্রুবক হতে হবে। * (জাভা উদাহরণ)

class PaycheckCalculator {
   float overtimeFactor;

   protected float setOvertimeFactor(float overtimeFactor) {
      this.overtimeFactor = overtimeFactor;
   }

   protected float getOvertimeFactor() {
      return overtimeFactor;
   }
}

অথবা

class PaycheckCalculator {
   public static final float OVERTIME_FACTOR = 1.5f;
}

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

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

দ্বিতীয়টি সেই ক্ষেত্রে কার্যকর যেখানে ধ্রুবকটি পরিবর্তিত হওয়ার সম্ভাবনা নেই তবে একাধিক স্থানে ব্যবহৃত হয়। পরিবর্তনের জন্য একটি ধ্রুবক থাকা (এটির সম্ভাব্য ঘটনাটি না হওয়া) পুরো জায়গা জুড়ে সেট করা বা সম্পত্তি ফাইল থেকে পাওয়ার চেয়ে এটি অনেক সহজ is

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


এটি একটি ডেটা পরিবর্তন, কোনও কোড পরিবর্তন নয়। ওভারটাইমের হারটি হার্ড কোডিং করা উচিত নয়।
জিম সি

আপনার গেট এবং সেটটি পিছনের দিকে রয়েছে বলে মনে হচ্ছে।
ম্যাসন হুইলার

উপস! পরীক্ষা করা উচিত ছিল ...
মাইকেল কে

2

আমি আপনার উদাহরণটিকে ওসিপির দুর্দান্ত প্রতিনিধিত্ব হিসাবে দেখছি না। আমি মনে করি নিয়মের আসলে কী অর্থ তা হল:

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

নীচে একটি দুর্বল বাস্তবায়ন। প্রতিবার আপনি গেম যুক্ত করার সময় আপনাকে গেমপ্লেয়ার ক্লাসটি সংশোধন করতে হবে।

class GamePlayer
{
   public void PlayGame(string game)
   {
      switch(game)
      {
          case "Poker":
              PlayPoker();
              break;

          case "Gin": 
              PlayGin();
              break;

          ...
      }
   }

   ...
}

গেমপ্লেয়ার ক্লাসটি কখনই সংশোধন করা উচিত নয়

class GamePlayer
{
    ...

    public void PlayGame(string game)
    {
        Game g = GameFactory.GetByName(game); 
        g.Play();   
    }

    ...
}

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

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

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

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


1

এই বিশেষ উদাহরণে, আপনার কাছে যা "ম্যাজিক মান" হিসাবে পরিচিত। মূলত একটি শক্ত কোডড মান যা সময়ের সাথে সাথে পরিবর্তন করতে পারে বা নাও পারে। আমি আপনার উদারতার সাথে যে ধরণের প্রতিক্রিয়া প্রকাশ করেছি তা সম্বোধন করার চেষ্টা করতে যাচ্ছি, তবে এটি এমন এক ধরণের উদাহরণ যা একটি ক্লাসে মান পরিবর্তনের চেয়ে সাবক্লাস তৈরি করা বেশি কাজ।

সম্ভবত আরও বেশি, আপনি আপনার শ্রেণিবৃত্তিতে খুব শীঘ্রই আচরণটি নির্দিষ্ট করেছেন।

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

বেস PaycheckCalculatorশ্রেণিতে আপনি এটি বিমূর্ত তৈরি করেন এবং আপনি যে পদ্ধতিগুলি প্রত্যাশা করেন তা নির্দিষ্ট করে দিন। মূল গণনা একই, এটি কেবলমাত্র নির্দিষ্ট কারণগুলি পৃথকভাবে গণনা করা হয়। তোমার HourlyPaycheckCalculatorতারপর বাস্তবায়ন করবে getOvertimeFactorপদ্ধতি এবং 1.5 অথবা 2.0 আপনার ক্ষেত্রে হতে পারে ফিরে যান। আপনার 1.0 ফিরিয়ে দেওয়ার StraightTimePaycheckCalculatorবাস্তবায়ন হবে getOvertimeFactor। অবশেষে তৃতীয় বাস্তবায়ন এমন হবে NoOvertimePaycheckCalculatorযা getOvertimeFactor0 প্রদানের বাস্তবায়ন করবে ।

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

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

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