এলএসপি বনাম ওসিপি / লিসকভ সাবস্টিটিউশন ভিএস ওপেন ক্লোজ


48

আমি ওওপির সলাইড নীতিগুলি বোঝার চেষ্টা করছি এবং আমি এই সিদ্ধান্তে পৌঁছেছি যে এলএসপি এবং ওসিপির কিছু মিল রয়েছে (যদি না আরও বলা হয়)।

মুক্ত / বদ্ধ নীতিতে বলা হয়েছে "সফ্টওয়্যার সত্তা (শ্রেণি, মডিউল, ফাংশন, ইত্যাদি) এক্সটেনশনের জন্য উন্মুক্ত হওয়া উচিত, তবে পরিবর্তনের জন্য বন্ধ করা উচিত"।

এলএসপিকে সহজ কথায় বলা হয়েছে যে যে কোনও উদাহরণ থেকে উদ্ভূত যে কোনও উদাহরণ থেকে Fooপ্রতিস্থাপন করা যেতে পারে এবং প্রোগ্রামটি একইভাবে কাজ করবে।BarFoo

আমি প্রো ওওপি প্রোগ্রামার নই, তবে আমার কাছে মনে হয় এলএসপি কেবল তখনই সম্ভব, যদি Barপ্রাপ্ত থেকে Fooএটিতে কোনও পরিবর্তন হয় না তবে কেবল এটি প্রসারিত করে। তার অর্থ হল যে বিশেষ প্রোগ্রামে এলএসপি কেবল তখনই সত্য যখন ওসিপি সত্য হয় এবং ওসিপি কেবল সত্য যদি এলএসপি সত্য হয়। তার মানে তারা সমান।

আমি ভুল হলে শুধরে. আমি সত্যিই এই ধারণা বুঝতে চাই। উত্তরের জন্য ধন্যবাদ।


4
এটি উভয় ধারণার একটি খুব সংকীর্ণ ব্যাখ্যা। খোলা / বন্ধ এখনও বজায় রাখা যায় এখনও এলএসপি লঙ্ঘন করে। আয়তক্ষেত্র / স্কোয়ার বা উপবৃত্তাকার / বৃত্তের উদাহরণগুলি ভাল চিত্রণ rations দু'জনই ওসিপি-র অনুসরণ করে, তবু দুজনেই এলএসপিকে লঙ্ঘন করে।
জোয়েল ইথারটন

1
বিশ্ব (বা কমপক্ষে ইন্টারনেট) এ নিয়ে বিভ্রান্ত। kirkk.com/modularity/2009/12/solid-prصولসমূহ- for-class-design । এই লোকটি বলে যে এলএসপি লঙ্ঘনও ওসিপি লঙ্ঘন। এবং তারপরে 156 পৃষ্ঠায় "সফটওয়্যার ইঞ্জিনিয়ারিং ডিজাইন: থিওরি এবং অনুশীলন" বইটিতে লেখক এমন কিছু উদাহরণ দিয়েছেন যা ওসিপিকে মান্য করে তবে এলএসপিকে লঙ্ঘন করে। আমি এই ছেড়ে দিয়েছি।
মনোজ আর

@ জোয়েলথের্টন এই জোড়াগুলি কেবলমাত্র এলএসপি লঙ্ঘন করে যদি তারা পরিবর্তনীয় হয়। অপরিবর্তনীয় ক্ষেত্রে, থেকে প্রাপ্ত Squareহওয়া Rectangleএলএসপি লঙ্ঘন করে না। (কিন্তু এটা সম্ভবত এখনও খারাপ নকশা অপরিবর্তনীয় ক্ষেত্রে যেহেতু আপনি বর্গ থাকতে পারে Rectangleগুলি করে একটি নয় Squareযা গণিত সাথে মেলে না)
CodesInChaos

সাধারণ উপমা (একটি গ্রন্থাগার লেখক-ব্যবহারকারীর দৃষ্টিকোণ থেকে)। এলএসপি হ'ল একটি পণ্য (গ্রন্থাগার) বিক্রির মতো যা এটি যা বলে তার 100% প্রয়োগ করার দাবি করে (ইন্টারফেসে বা ব্যবহারকারী ম্যানুয়ালটিতে), তবে বাস্তবে (বা যা বলা হয় তার সাথে মেলে না) doesn't ওসিপি একটি পণ্য (গ্রন্থাগার) বিক্রির মতো প্রতিশ্রুতি দিয়ে থাকে যে যখন নতুন কার্যকারিতা (ফার্মওয়্যারের মতো) প্রকাশিত হয় তখন এটি আপগ্রেড (বাড়ানো) হতে পারে তবে কারখানার পরিষেবা ছাড়া আসলে আপগ্রেড করা যায় না।
rwong

উত্তর:


119

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

পার্থক্যগুলি আরও ব্যাখ্যা করা হবে তবে প্রথমে আসুন নীতির মধ্যে ডুব দেই:

উন্মুক্ত-বন্ধ নীতি (ওসিপি)

চাচা বব এর মতে :

আপনার কোনও শ্রেণিবিস্তার আচরণটি পরিবর্তন না করে প্রসারিত করতে সক্ষম হওয়া উচিত।

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

মূলটি 1988 সালে বার্ট্র্যান্ড মেয়ারের কাছ থেকে এসেছে:

সফ্টওয়্যার সত্তা (শ্রেণি, মডিউল, ফাংশন, ইত্যাদি) এক্সটেনশনের জন্য উন্মুক্ত হওয়া উচিত, তবে পরিবর্তনের জন্য বন্ধ করা উচিত।

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

// Context is closed for modifications. Meaning you are
// not supposed to change the code here.
public class Context {

    // Context is however open for extension through
    // this private field
    private IBehavior behavior;

    // The context calls the behavior in this public 
    // method. If you want to change this you need
    // to implement it in the IBehavior object
    public void doStuff() {
        if (this.behavior != null)
            this.behavior.doStuff();
    }

    // You can dynamically set a new behavior at will
    public void setBehavior(IBehavior behavior) {
        this.behavior = behavior;
    }
}

// The extension point looks like this and can be
// subclassed/implemented
public interface IBehavior {
    public void doStuff();
}

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

অর্থাৎ প্রসঙ্গ শ্রেণিটি পরিবর্তনের জন্য বন্ধ থাকলেও এক্সটেনশনের জন্য উন্মুক্ত । এটি আসলে অন্য একটি মূল নীতি অনুসরণ করে কারণ আমরা উত্তরাধিকারের পরিবর্তে অবজেক্ট কম্পোজিশনের সাথে আচরণটি রাখছি:

"প্রিয় শ্রেণীর উত্তরাধিকারের উপর ' অবজেক্ট রচনা '। (গ্যাং অফ ফোর 1995: 20)

আমি পাঠককে সেই নীতিটি পড়তে দেব কারণ এটি এই প্রশ্নের ক্ষেত্রের বাইরে। উদাহরণ সহকারে চালিয়ে যেতে, বলুন আমাদের আইবিহেভিয়ার ইন্টারফেসের নিম্নলিখিত প্রয়োগ রয়েছে:

public class HelloWorldBehavior implements IBehavior {
    public void doStuff() {
        System.println("Hello world!");
    }
}

public class GoodByeBehavior implements IBehavior {
    public void doStuff() {
        System.out.println("Good bye cruel world!");
    }
}

এই প্যাটার্নটি ব্যবহার করে আমরা রানটাইমে প্রসঙ্গের আচরণটি setBehaviorএক্সটেনশন পয়েন্ট হিসাবে পদ্ধতিতে পরিবর্তন করতে পারি ।

// in your main method
Context c = new Context();

c.setBehavior(new HelloWorldBehavior());
c.doStuff();
// prints out "Hello world!"

c.setBehavior(new GoodByeBehavior());
c.doStuff();
// prints out "Good bye cruel world!"

সুতরাং আপনি যখনই "বদ্ধ" প্রসঙ্গ শ্রেণি প্রসারিত করতে চান, এটি "উন্মুক্ত" সহযোগিতা নির্ভরতা উপশ্রুত করে এটি করুন। এটি স্পষ্টতই প্রসঙ্গটি সাবক্লাসিংয়ের মতো নয় তবে এটি ওসিপি। এলএসপিও এই সম্পর্কে কোন উল্লেখ করে না।

উত্তরাধিকারের পরিবর্তে মিক্সিনগুলি দিয়ে প্রসারিত করা

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

এখানে মিক্সিনের জাভাস্ক্রিপ্ট উদাহরণ যা অ্যাঙ্গরগুলির জন্য একটি সাধারণ এইচটিএমএল টেমপ্লেট রেন্ডার করে:

// The mixin, provides a template for anchor HTML elements, i.e. <a>
var LinkMixin = {
    render: function() {
        return '<a href="' + this.link +'">'
            + this.content 
            + '</a>;
    }
}

// Constructor for a youtube link
var YoutubeLink = function(content, youtubeId) {
    this.content = content;
    this.setLink(this.youtubeId);
};
// Methods are added to the prototype
YoutubeLink.prototype = {
    setLink: function(youtubeid) {
        this.link = 'http://www.youtube.com/watch?v=' + youtubeid;
    }
};
// Extend YoutubeLink prototype with the LinkMixin using
// underscore/lodash extend
_.extend(YoutubeLink.protoype, LinkMixin);

// When used:
var ytLink = new YoutubeLink("Cool Movie!", "idOaZpX8lnA");

console.log(ytLink.render());
// will output: 
// <a href="http://www.youtube.com/watch?=vidOaZpX8lnA">Cool Movie!</a>

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

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

মনে রাখবেন যে এই পদ্ধতিতে একাধিক উত্তরাধিকার সম্পাদন করা সম্ভব কারণ বেশিরভাগ extendবাস্তবায়ন একাধিক বস্তুর মিশ্রণ করতে পারে:

_.extend(MyClass, Mixin1, Mixin2 /* [, ...] */);

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

লিসকভের সাবস্টিটিউশন নীতি (এলএসপি)

চাচা বব এটিকে সহজভাবে সংজ্ঞায়িত করেছেন:

উত্পন্ন ক্লাসগুলি অবশ্যই তাদের বেস ক্লাসের জন্য পরিবর্তনযোগ্য হতে হবে।

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

কি এখানে চেয়েছিলেন নিম্নলিখিত প্রতিকল্পন সম্পত্তি ভালো কিছু হল: প্রতিটি বস্তুর জন্য যদি o1ধরনের Sএকটি বস্তু আছে o2ধরনের Tএমন সব প্রোগ্রামের জন্য যে Pপদ সংজ্ঞায়িত T, আচরণকে Pঅপরিবর্তিত যখন o1প্রতিস্থাপিত হয় o2তারপর Sএকটি উপপ্রকার হয় T

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

  • একইভাবে গণনা করা দরকার,
  • একই আচরণ, এবং
  • অন্যথায় কোনওভাবে সম্পূর্ণ ভিন্ন

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

যখনই আপনি কোনও জিনিসের সাব টাইপ পরীক্ষা করার তাগিদ অনুভব করছেন, আপনি সম্ভবত এটি ভুল করেছেন।

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

আসল সমস্যার উপর নির্ভর করে এই "ডিজাইন ভুল" এর চারপাশে উপায় রয়েছে:

  • সুপার ক্লাস পূর্বশর্তগুলি কল করছে না, পরিবর্তে কলকারীকে এটি করতে বাধ্য করছে।
  • সুপার ক্লাসে কলকারীটির প্রয়োজনীয় একটি জেনেরিক পদ্ধতি অনুপস্থিত।

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

আমি আসলে ভিজিটর প্যাটার্নটি অনেক পছন্দ করি কারণ এটি যদি ইফ-স্টেটমেন্ট স্প্যাগেটির বড় যত্ন নিতে পারে এবং আপনি বিদ্যমান কোডটিতে যা ভাবেন তার চেয়ে এটি কার্যকর করা সহজ। বলুন আমাদের নীচের প্রসঙ্গ রয়েছে:

public class Context {

    public void doStuff(string query) {

        // outcome no. 1
        if (query.Equals("Hello")) {
            System.out.println("Hello world!");
        } 

        // outcome no. 2
        else if (query.Equals("Bye")) {
            System.out.println("Good bye cruel world!");
        }

        // a change request may require another outcome...

    }

}

// usage:
Context c = new Context();

c.doStuff("Hello");
// prints "Hello world"

c.doStuff("Bye");
// prints "Bye"

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

public interface IVisitor {
    public bool canDo(string query);
    public void doStuff();
}

// outcome 1
public class HelloVisitor implements IVisitor {
    public bool canDo(string query) {
        return query.Equals("Hello");
    }
    public void doStuff() {
         System.out.println("Hello World");
    }
}

// outcome 2
public class ByeVisitor implements IVisitor {
    public bool canDo(string query) {
        return query.Equals("Bye");
    }
    public void doStuff() {
        System.out.println("Good bye cruel world");
    }
}

এই মুহুর্তে, প্রোগ্রামার যদি ভিজিটর প্যাটার্ন সম্পর্কে জানতে না পারে, তবে তিনি প্রসঙ্গটি ক্লাসটি কিছু নির্দিষ্ট ধরণের কিনা তা পরীক্ষা করার জন্য প্রয়োগ করতেন। ভিজিটর ক্লাসগুলির একটি বুলিয়ান canDoপদ্ধতি রয়েছে, তাই বাস্তবায়নকারী সেই পদ্ধতি কলটি ব্যবহার করে এটি কাজ করার সঠিক বিষয় কিনা তা নির্ধারণ করতে পারেন। প্রসঙ্গ শ্রেণি সমস্ত দর্শনার্থী (এবং নতুন যুক্ত করতে) এর মতো ব্যবহার করতে পারে:

public class Context {
    private ArrayList<IVisitor> visitors = new ArrayList<IVisitor>();

    public Context() {
        visitors.add(new HelloVisitor());
        visitors.add(new ByeVisitor());
    }

    // instead of if-statements, go through all visitors
    // and use the canDo method to determine if the 
    // visitor object is the right one to "visit"
    public void doStuff(string query) {
        for(IVisitor visitor : visitors) {
            if (visitor.canDo(query)) {
                visitor.doStuff();
                break;
                // or return... it depends if you have logic 
                // after this foreach loop
            }
        }
    }

    // dynamically adds new visitors
    public void addVisitor(IVisitor visitor) {
        if (visitor != null)
            visitors.add(visitor);
    }
}

উভয় নিদর্শনগুলি ওসিপি এবং এলএসপিকে অনুসরণ করে, তবে তারা উভয়ই তাদের সম্পর্কে বিভিন্ন বিষয় নির্দেশ করে। সুতরাং কোডটি যদি নীতিগুলির মধ্যে একটি লঙ্ঘন করে তবে এর মতো দেখতে কেমন?

একটি নীতি লঙ্ঘন কিন্তু অন্য অনুসরণ

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

ওসিপি অনুসরণ করে তবে এলএসপি অনুসরণ করে না

যাক আমাদের দেওয়া কোডটি আছে:

public interface IPerson {}

public class Boss implements IPerson {
    public void doBossStuff() { ... }
}

public class Peon implements IPerson {
    public void doPeonStuff() { ... }
}

public class Context {
    public Collection<IPerson> getPersons() { ... }
}

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

// in some routine that needs to do stuff with 
// a collection of IPerson:
Collection<IPerson> persons = context.getPersons();
for (IPerson person : persons) {
    // now we have to check the type... :-P
    if (person instanceof Boss) {
        ((Boss) person).doBossStuff();
    }
    else if (person instanceof Peon) {
        ((Peon) person).doPeonStuff();
    }
}

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

public class Boss implements IPerson {
    // we're adding this general method
    public void doStuff() {
        // that does the call instead
        this.doBossStuff();
    }
    public void doBossStuff() { ... }
}


public interface IPerson {
    // pulled up method from Boss
    public void doStuff();
}

// do the same for Peon

এখন সুবিধাটি হ'ল এলএসপিকে অনুসরণ করে আপনার আর সঠিক ধরণেরটি জানতে হবে না:

// in some routine that needs to do stuff with 
// a collection of IPerson:
Collection<IPerson> persons = context.getPersons();
for (IPerson person : persons) {
    // yay, no type checking!
    person.doStuff();
}

এলএসপি অনুসরণ করে তবে ওসিপি নয়

আসুন এমন কোনও কোড দেখুন যা এলএসপি অনুসরণ করে তবে ওসিপি নয়, এটি একধরনের বিষয়বস্তুযুক্ত তবে আমার এটি সহ্য করুন এটি খুব সূক্ষ্ম ভুল:

public class LiskovBase {
    public void doStuff() {
        System.out.println("My name is Liskov");
    }
}

public class LiskovSub extends LiskovBase {
    public void doStuff() {
        System.out.println("I'm a sub Liskov!");
    }
}

public class Context {
    private LiskovBase base;

    // the good stuff
    public void doLiskovyStuff() {
        base.doStuff();
    }

    public void setBase(LiskovBase base) { this.base = base }
}

কোডটি এলএসপি করে কারণ প্রসঙ্গটি প্রকৃত প্রকারটি না জেনে লিসকভবেস ব্যবহার করতে পারে। আপনি কি ভাববেন যে এই কোডটি ওসিপি অনুসরণ করে তবে ঘনিষ্ঠভাবে দেখুন, ক্লাসটি কি সত্যিই বন্ধ রয়েছে ? doStuffপদ্ধতিটি যদি কেবল একটি লাইন প্রিন্ট করার চেয়ে আরও বেশি কিছু করে?

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

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

public class LiskovBase {
    // this is now a template method
    // the code that was duplicated
    public final void doStuff() {
        System.out.println(getStuffString());
    }

    // extension point, the code that "varies"
    // in LiskovBase and it's subclasses
    // called by the template method above
    // we expect it to be virtual and overridden
    public string getStuffString() {
        return "My name is Liskov";
    }
}

public class LiskovSub extends LiskovBase {
    // the extension overridden
    // the actual code that varied
    public string getStuffString() {
        return "I'm sub Liskov!";
    }
}

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

উপসংহার

আমি আশা করি এটি সমস্তই ওসিপি এবং এলএসপি এবং তাদের মধ্যে পার্থক্য / সাদৃশ্য সম্পর্কিত কিছু প্রশ্ন মুছে ফেলে। এগুলিকে সমান হিসাবে বরখাস্ত করা সহজ তবে উপরের উদাহরণগুলিতে দেখানো উচিত যে তারা তা নয়।

নোট করুন, উপরে নমুনা কোড থেকে সংগ্রহ:

  • ওসিপি কার্যকরী কোডটি লক করার বিষয়ে রয়েছে তবে এটি কোনও প্রকারের এক্সটেনশন পয়েন্টগুলির সাথে কোনওভাবে খোলা রাখবে।

    এটি হ'ল টেমপ্লেট পদ্ধতিটির প্যাটার্নের উদাহরণ হিসাবে কোড পরিবর্তন করে কোড নকল করে এড়ানো। এটি দ্রুত ব্যর্থ হওয়ার জন্য যেমন ব্রেকিং পরিবর্তনগুলি বেদনাদায়ক হয় (যেমন একটি জায়গা পরিবর্তন করুন, এটিকে অন্য কোথাও ভাঙ্গুন) মঞ্জুরি দেয়। রক্ষণাবেক্ষণের প্রয়োজনে এনক্যাপসুলেটিং পরিবর্তনের ধারণাটি একটি ভাল জিনিস, কারণ পরিবর্তনগুলি সর্বদা ঘটে।

  • এলএসপি ব্যবহারকারীকে বিভিন্ন বস্তু পরিচালনা করতে দেয় যা প্রকৃত ধরনটি কী তা পরীক্ষা না করে সুপারটাইপ প্রয়োগ করে। পলিমারফিজমটি সহজাতভাবে এটি ।

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


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

5
এটি আমি দেখেছি সেরা স্ট্যাক এক্সচেঞ্জের উত্তরগুলির মধ্যে একটি। আমি আশা করি আমি এটি 10 ​​বার upvote করতে পারে। ভাল হয়েছে, এবং দুর্দান্ত ব্যাখ্যার জন্য আপনাকে ধন্যবাদ।
বব হর্ন

সেখানে আমি জাভাস্ক্রিপ্টে একটি ব্লার্ব যুক্ত করেছি যা ক্লাস-ভিত্তিক প্রোগ্রামিং ভাষা নয় তবে তবুও এলএসপি অনুসরণ করতে এবং পাঠ্য সম্পাদনা করতে পারে তাই আশা করি এটি আরও সাবলীলভাবে পড়া যায় reads রাম রাম!
স্পোকাইক

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

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

15

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

ওসিপি যা ঠিক করার চেষ্টা করে

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

যে সমস্যা আছে

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

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

প্রথমটি, পদ্ধতিগত সমাধানের সাথে সমস্যাটি প্রতিটি বইয়ের জন্য অতিরিক্ত ক্ষেত্র এবং অনেক ক্ষেত্রে অতিরিক্ত অতিরিক্ত বাড়াবাড়ি জটিলতা। দ্বিতীয় সমাধানটি কেবল যুক্তিযুক্তিকে বাধ্য করে যেখানে এটি আসলে প্রয়োজনীয়।

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

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

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

এলএসপি যা ঠিক করার চেষ্টা করে

আমরা সবাই কোড পুনরায় ব্যবহার পছন্দ করি। একটি রোগ যা এর পরে আসে তা হ'ল অনেক প্রোগ্রাম এটিকে সম্পূর্ণরূপে বুঝতে পারে না, যেখানে তারা অন্ধভাবে কোডের সাধারণ লাইনগুলিকে অনাবৃত করে কেবল অপঠনযোগ্য জটিলতা তৈরি করতে এবং মডিউলগুলির মধ্যে অপ্রয়োজনীয় আঁটসাঁটো মিলন তৈরি করে যা কিছু কোডের লাইন ছাড়া অন্য, ধারণাগত কাজ-করণ করা যতটা সাধারণ যায় তেমন কিছুই মিল নেই।

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

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

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

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


1
আমি এই এবং স্পোইকের উত্তরগুলি দারুণভাবে সাফল্য অর্জন করতে সক্ষম হয়েছি - দুর্দান্ত কাজ।
ডেভিড কাল্প

7

আমার বোঝার থেকে:

ওসিপি বলেছেন: "আপনি যদি নতুন কার্যকারিতা যুক্ত করেন তবে এটি পরিবর্তনের পরিবর্তে একটি বিদ্যমান শ্রেণি বাড়িয়ে একটি নতুন শ্রেণি তৈরি করুন" "

এলএসপি বলেছেন: "আপনি যদি বিদ্যমান ক্লাসকে প্রসারিত করে একটি নতুন শ্রেণি তৈরি করেন তবে নিশ্চিত করুন যে এটির বেসের সাথে এটি সম্পূর্ণরূপে আদান-প্রদানযোগ্য" "

সুতরাং আমি মনে করি তারা একে অপরের পরিপূরক তবে তারা সমান নয়।


4

যদিও এটি সত্য যে ওসিপি এবং এলএসপি উভয়কেই সংশোধন করার দরকার আছে, ওসিপি যে ধরনের পরিবর্তন সম্পর্কে কথা বলে সেগুলিই কোনও এলএসপি সম্পর্কে আলোচনা করে না।

ওসিপি সম্পর্কিত পরিবর্তন করা একটি বিদ্যমান শ্রেণীর বিকাশকারী রাইটিং কোডের শারীরিক ক্রিয়া ।

এলএসপি তার বর্গ শ্রেণীর তুলনায় একটি উদ্ভূত শ্রেণি নিয়ে আসে এমন আচরণের সংশোধন, এবং প্রোগ্রামের সম্পাদনের রানটাইম পরিবর্তন যা সুপারক্লাসের পরিবর্তে সাবক্লাস ব্যবহার করে হতে পারে deals

সুতরাং যদিও তারা দূর থেকে OCP! = LSP থেকে একই দেখাচ্ছে similar আসলে আমি মনে করি এগুলি কেবলমাত্র দুটি সলিড নীতি যা একে অপরের শর্তে বোঝা যায় না।


2

এলএসপিকে সহজ কথায় বলা হয়েছে যে ফু এর যে কোনও দৃষ্টান্ত প্রোগ্রামের কার্যকারিতা ক্ষতি ছাড়াই ফু থেকে প্রাপ্ত বারের যে কোনও উদাহরণ দিয়ে প্রতিস্থাপন করা যেতে পারে।

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

তবে শেষ পর্যন্ত, এটি অর্জন করা সাধারণত কঠিন, কারণ বেশিরভাগ সময়, ফু ব্যবহার করে কোডটি তার সমস্ত আচরণের উপর নির্ভর করে। সুতরাং এটি অপসারণ করা এলএসপি লঙ্ঘন করে। তবে এটিকে সরলীকৃত করা কেবল এলএসপির অংশ।


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

0

লঙ্ঘন করতে পারে এমন অবজেক্ট সম্পর্কে

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

কে এলএসপি লঙ্ঘন করতে পারে

যখন কোনও চুক্তি এবং সেই ইন্টারফেসটির প্রয়োগকরণের সাথে ইন্টারফেস থাকে তখনই এলএসপি ভেঙে যায় কিনা তা পরীক্ষা করতে পারে। যদি প্রয়োগটি ইন্টারফেসের সাথে বা সাধারণভাবে কথা বলার জন্য চুক্তির সাথে সামঞ্জস্য না করে তবে এলএসপি নষ্ট হয়ে যায়।

সহজ উদাহরণ:

class Container {
    // Should add the object to the container.
    void addObject(object) {
        internalArray.append(object);
    }

    int size() {
        return internalArray.size();
    }
}

class CustomContainer extends Container {
    @Override void addObject(object) {
        System.console.print("Skipping object! Ha-ha!");
    }
}

void fillWithRandomNumbers(Container container) {
    while (container.size() < 42) {
        container.addObject(Randomizer.getNumber())
    }
}

চুক্তিতে স্পষ্টভাবে বলা হয়েছে যে addObjectধারককে তার যুক্তি যুক্ত করতে হবে। এবং CustomContainerস্পষ্টভাবে যে চুক্তি ভঙ্গ। সুতরাং CustomContainer.addObjectফাংশন এলএসপি লঙ্ঘন করে। এভাবে CustomContainerক্লাসটি এলএসপি লঙ্ঘন করে। সর্বাধিক গুরুত্বপূর্ণ পরিণতিটি এটি CustomContainerপাস করা যায় না fillWithRandomNumbers()Containerপ্রতিস্থাপন করা যাবে না CustomContainer

একটি খুব গুরুত্বপূর্ণ বিষয় মনে রাখবেন। এটি সম্পূর্ণ কোড নয় যা এলএসপিকে ভঙ্গ করে, এটি বিশেষত CustomContainer.addObjectএবং সাধারণত CustomContainerযে এলএসপিকে ভঙ্গ করে। যখন আপনি বলেন যে এলএসপি লঙ্ঘিত হয়েছে তখন আপনার সর্বদা দুটি জিনিস নির্দিষ্ট করা উচিত:

  • এলএসপি লঙ্ঘনকারী সত্তা।
  • সত্তা দ্বারা চুক্তি যা ভেঙে গেছে।

এটাই. কেবল একটি চুক্তি এবং এর বাস্তবায়ন। কোডে একটি হতাশা এলএসপি লঙ্ঘন সম্পর্কে কিছুই বলে না।

কে ওসিপি লঙ্ঘন করতে পারে

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

জটিল মনে হচ্ছে। আসুন একটি সহজ উদাহরণ চেষ্টা করুন:

enum Platform {
    iOS,
    Android
}

class PlatformDescriber {
    String describe(Platform platform) {
        switch (platform) {
            case iOS: return "iPhone OS, v10.0.1";
            case Android: return "Android, v7.1";
        }
    }
}

ডেটা সেট হ'ল সমর্থিত প্ল্যাটফর্মগুলির সেট। PlatformDescriberডেটা সেট থেকে মানগুলি পরিচালনা করে এমন উপাদান। একটি নতুন প্ল্যাটফর্ম যুক্ত করার জন্য উত্সের কোডটি আপডেট করা দরকার PlatformDescriber। এভাবে PlatformDescriberক্লাসটি ওসিপি লঙ্ঘন করে।

আরেকটি উদাহরণ:

class Shop {
    void sellItemToCustomer(item, customer) {
        // some buisiness logic here
        ...
        logger.logItemSold()
    }
}

class Logger {
    void logItemSold() {
        logger.logToStdErr("an item was sold")
        logger.logToRemote("an item was sold")
        logger.logToDatabase("an item was sold")
    }
}

"ডেটা সেট" হ'ল চ্যানেলের সেট যেখানে লগ এন্ট্রি যুক্ত করা উচিত। Loggerএমন উপাদান যা সমস্ত চ্যানেলে এন্ট্রি যুক্ত করার জন্য দায়ী। লগিংয়ের অন্য উপায়ে সমর্থন যুক্ত করার জন্য উত্সের কোডটি আপডেট করা দরকার Logger। এভাবে Loggerক্লাসটি ওসিপি লঙ্ঘন করে।

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

সিমানা আতিক্রম

এখন জটিল অংশ। উপরের উদাহরণগুলির সাথে নিম্নলিখিতগুলির সাথে তুলনা করুন:

enum GregorianWeekDay {
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}

String translateToRussian(GregorianWeekDay weekDay) {
    switch (weekDay) {
        case Monday: return "Понедельник";
        case Tuesday: return "Вторник";
        case Wednesday: return "Среда";
        case Thursday: return "Четверг";
        case Friday: return "Пятница";
        case Saturday: return "Суббота";
        case Sunday: return "Воскресенье";
    }
}

আপনি translateToRussianOCP লঙ্ঘন করতে পারেন । তবে আসলে তা নয়। GregorianWeekDayসঠিক নাম সহ ঠিক 7 সপ্তাহের নির্দিষ্ট সীমা রয়েছে। এবং গুরুত্বপূর্ণ বিষয় হ'ল এই সীমাগুলি শব্দার্থভাবে সময়ের সাথে সাথে পরিবর্তন করতে পারে না। গ্রেগরিয়ান সপ্তাহে সর্বদা 7 দিন থাকবে। সর্বদা সোমবার, মঙ্গলবার ইত্যাদি থাকবে This এই ডেটা সেটটি শব্দার্থিকভাবে স্থির করা হয়েছে। এটি সম্ভব নয় যে translateToRussianএর উত্স কোডের জন্য সংশোধন প্রয়োজন। সুতরাং ওসিপি লঙ্ঘন হয় না।

এখন এটি পরিষ্কার হওয়া উচিত যে ক্লান্তিকর switchবিবৃতি সর্বদা ভাঙা ওসিপি-র ইঙ্গিত দেয় না।

পার্থক্য

এখন পার্থক্যটি অনুভব করুন:

  • এলএসপির বিষয় হ'ল "ইন্টারফেস / চুক্তি বাস্তবায়ন"। যদি বাস্তবায়ন চুক্তির সাথে সামঞ্জস্য না করে তবে এটি এলএসপিকে ভেঙে দেয়। এটি কার্যকর নয় যদি সেই বাস্তবায়ন সময়ের সাথে পরিবর্তিত হতে পারে বা না, যদি এটি সম্প্রসারিত হয় বা না হয়।
  • ওসিপির বিষয় হ'ল "প্রয়োজনীয় পরিবর্তনের সাড়া দেওয়ার উপায়"। যদি কোনও নতুন ধরণের ডেটা সমর্থন করার জন্য সেই উপাদানটির সোর্স কোড পরিবর্তন করতে হয় যা সেই ডেটা পরিচালনা করে, তবে সেই উপাদানটি ওসিপি বিভক্ত করে। উপাদানটি তার চুক্তিটি ভেঙে দেয় বা না করা গুরুত্বপূর্ণ নয়।

এই অবস্থাগুলি সম্পূর্ণরূপে অর্থেগোনাল are

উদাহরণ

ইন @ Spoike এর উত্তর এক নীতি লঙ্ঘন কিন্তু অন্যান্য নিম্নলিখিত অংশ সম্পূর্ণভাবে ভুল।

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

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

আসুন ওসিপি বা এলএসপির সত্য লঙ্ঘন সহ কিছু সঠিক উদাহরণ দেওয়ার চেষ্টা করি।

ওসিপি অনুসরণ করুন তবে এলএসপি নয়

interface Platform {
    String name();
    String version();
}

class iOS implements Platform {
    @Override String name() { return "iOS"; }
    @Override String version() { return "10.0.1"; }
}

interface PlatformSerializer {
    String toJson(Platform platform);
}

class HumanReadablePlatformSerializer implements PlatformSerializer {
    String toJson(Platform platform) {
        return platform.name() + ", v" + platform.version();
    }
}

এখানে, HumanReadablePlatformSerializerযখন নতুন প্ল্যাটফর্ম যুক্ত করা হয় তখন কোনও পরিবর্তনের প্রয়োজন হয় না। সুতরাং এটি ওসিপি অনুসরণ করে।

তবে চুক্তির জন্য toJsonঅবশ্যই সঠিকভাবে ফর্ম্যাট করা জেএসওএন ফেরত আসতে হবে। ক্লাস তা করে না। যে কারণে এটি এমন কোনও উপাদানটিতে যেতে পারে না যা PlatformSerializerকোনও নেটওয়ার্কের অনুরোধের মূলধারাকে ফর্ম্যাট করতে ব্যবহার করে। এইভাবে HumanReadablePlatformSerializerএলএসপি লঙ্ঘন করে।

এলএসপি অনুসরণ করুন তবে ওসিপি নয়

পূর্ববর্তী উদাহরণে কিছু পরিবর্তন:

class Android implements Platform {
    @Override String name() { return "Android"; }
    @Override String version() { return "7.1"; }
}
class HumanReadablePlatformSerializer implements PlatformSerializer {
    String toJson(Platform platform) {
        return "{ "
                + "\"name\": \"" + platform.name() + "\","
                + "\"version\": \"" + platform.version() + "\","
                + "\"most-popular\": " + isMostPopular(platform) + ","
                + "}"
    }

    boolean isMostPopular(Platform platform) {
        return (platform instanceof Android)
    }
}

সিরিয়ালাইজারটি সঠিকভাবে ফর্ম্যাট করা JSON স্ট্রিংটি ফেরত দেয়। সুতরাং, এখানে কোনও এলএসপি লঙ্ঘন নেই।

তবে একটি আবশ্যকতা রয়েছে যে প্ল্যাটফর্মটি যদি বেশিরভাগ ক্ষেত্রে ব্যবহৃত হয় তবে জেএসএনে সংশ্লিষ্ট সূত্র থাকতে হবে। এই উদাহরণে ওসিপি HumanReadablePlatformSerializer.isMostPopularফাংশন দ্বারা লঙ্ঘন করা হয় কারণ কোনও দিন আইওএস সবচেয়ে জনপ্রিয় প্ল্যাটফর্ম হয়ে যায়। আনুষ্ঠানিকভাবে এর অর্থ হল যে সর্বাধিক ব্যবহৃত প্ল্যাটফর্মগুলির সেটটি আপাতত "অ্যান্ড্রয়েড" হিসাবে সংজ্ঞায়িত করা হয়েছে এবং isMostPopularঅপ্রতুলভাবে সেই ডেটা সেটটি পরিচালনা করে। ডেটা সেটটি শব্দার্থগতভাবে স্থির হয়নি এবং সময়ের সাথে অবাধে পরিবর্তন হতে পারে। HumanReadablePlatformSerializerপরিবর্তনের ক্ষেত্রে এর উত্স কোডটি আপডেট করা দরকার।

আপনি এই উদাহরণে একক দায়িত্বের লঙ্ঘনও লক্ষ্য করতে পারেন। আমি ইচ্ছাকৃতভাবে একই বিষয় সত্তা উভয় নীতি প্রদর্শন করতে সক্ষম হওয়ায়। এসআরপি ঠিক করতে isMostPopularআপনি কিছু বাহ্যিকের সাথে ফাংশনটি বের করতে Helperএবং এতে একটি প্যারামিটার যুক্ত করতে পারেন PlatformSerializer.toJson। তবে এটি অন্য গল্প।


0

এলএসপি এবং ওসিপি এক নয়।

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

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

সুতরাং এটি এলএসপি এবং ওসিপির মধ্যে মূল পার্থক্য। প্রাক্তনটি কেবল কোড বেসটিকে যেমন দাঁড়ায় তেমন বৈধতা দেয়, পরেরটি কেবল একটি কোড বেস ব-দ্বীপটিকে এক সংস্করণ থেকে পরের সংস্করণে বৈধতা দেয় । যেমন তারা একই জিনিস হতে পারে না, তারা বিভিন্ন জিনিস বৈধতা হিসাবে সংজ্ঞায়িত করা হয়।

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


-1

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


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

এটি পূর্ববর্তী answers উত্তরগুলিতে তৈরি এবং ব্যাখ্যা করা পয়েন্টগুলির
গ্রিনট

1
মনে হচ্ছে আপনি কেবল নীতিগুলির একটি ব্যাখ্যা করছেন, আমি মনে করি এল। এটি ঠিক আছে তবে প্রশ্নটি দুটি ভিন্ন নীতির তুলনা / বিপরীতে চেয়েছিল। কেউ কেউ এটিকে অবমূল্যায়ন করেছেন বলেই সম্ভবত।
স্টারওয়েভার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.