জাভা: এমন একটি পদক্ষেপ নির্মাতা কীভাবে কার্যকর করবেন যার জন্য সেটারের অর্ডার কোনও ব্যাপার না?


10

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

আপনার Carমত একটি ইন্টারফেস আছে কল্পনা করুন :

public interface Car {
    public Engine getEngine(); // required
    public Transmission getTransmission(); // required
    public Stereo getStereo(); // optional
}

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

এটি একটি পদক্ষেপ নির্মাতার জন্য কল । সাধারণত আপনি এরকম কিছু বাস্তবায়ন করতেন:

public interface Car {
    public Engine getEngine(); // required
    public Transmission getTransmission(); // required
    public Stereo getStereo(); // optional

    public class Builder {
        public BuilderWithEngine engine(Engine engine) {
            return new BuilderWithEngine(engine);
        }
    }

    public class BuilderWithEngine {
        private Engine engine;
        private BuilderWithEngine(Engine engine) {
            this.engine = engine;
        }
        public BuilderWithEngine engine(Engine engine) {
            this.engine = engine;
            return this;
        }
        public CompleteBuilder transmission(Transmission transmission) {
            return new CompleteBuilder(engine, transmission);
        }
    }

    public class CompleteBuilder {
        private Engine engine;
        private Transmission transmission;
        private Stereo stereo = null;
        private CompleteBuilder(Engine engine, Transmission transmission) {
            this.engine = engine;
            this.transmission = transmission;
        }
        public CompleteBuilder engine(Engine engine) {
            this.engine = engine;
            return this;
        }
        public CompleteBuilder transmission(Transmission transmission) {
            this.transmission = transmission;
            return this;
        }
        public CompleteBuilder stereo(Stereo stereo) {
            this.stereo = stereo;
            return this;
        }
        public Car build() {
            return new Car() {
                @Override
                public Engine getEngine() {
                    return engine;
                }
                @Override
                public Transmission getTransmission() {
                    return transmission;
                }
                @Override
                public Stereo getStereo() {
                    return stereo;
                }
            };
        }
    }
}

(বিভিন্ন রচয়িতা ক্লাস একটি চেইন এর Builder, BuilderWithEngine, CompleteBuilder), যে অ্যাড এক প্রয়োজনীয় একের পর সেটার পদ্ধতি, পাশাপাশি সব ঐচ্ছিক সেটার পদ্ধতি ধারণকারী গত বর্গ সঙ্গে।
এর অর্থ এই পদক্ষেপ নির্মাতার ব্যবহারকারীরা সেই আদেশে সীমাবদ্ধ যা লেখক বাধ্যতামূলক সেটটারগুলি উপলব্ধ করেছিলেন । এখানে সম্ভাব্য ব্যবহারের একটি উদাহরণ দেওয়া আছে (নোট করুন যে সেগুলি সমস্ত কঠোরভাবে অর্ডার করা হয়েছে: engine(e)প্রথমে, তারপরে অনুসরণ করা হবে transmission(t)এবং শেষ পর্যন্ত alচ্ছিক stereo(s))।

new Builder().engine(e).transmission(t).build();
new Builder().engine(e).transmission(t).stereo(s).build();
new Builder().engine(e).engine(e).transmission(t).stereo(s).build();
new Builder().engine(e).transmission(t).engine(e).stereo(s).build();
new Builder().engine(e).transmission(t).stereo(s).engine(e).build();
new Builder().engine(e).transmission(t).transmission(t).stereo(s).build();
new Builder().engine(e).transmission(t).stereo(s).transmission(t).build();
new Builder().engine(e).transmission(t).stereo(s).stereo(s).build();

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

আমি এর জন্য কেবলমাত্র সমাধানটিই ভাবতে পারি এটি অত্যন্ত সংশ্লেষযুক্ত: বাধ্যতামূলক সম্পত্তিগুলির প্রতিটি সমন্বয়ের জন্য সেট করা হয়েছে বা এখনও সেট করা হয়নি, আমি একটি ডেডিকেটেড বিল্ডার শ্রেণি তৈরি করেছি যা জানে যে কোন সম্ভাব্য অন্যান্য বাধ্যতামূলক সেটটারগুলিতে পৌঁছানোর আগে ডাকা দরকার knows রাষ্ট্র যেখানে build()পদ্ধতিটি উপলব্ধ উচিত, এবং যারা setters প্রতিটি রচয়িতা একটি সম্পূর্ণ টাইপ যা এক ধাপ একটি ধারণকারী কাছাকাছি ফেরৎ build()পদ্ধতি।
আমি নীচের কোডটি যুক্ত করেছি, তবে আপনি বলতে পারেন যে আমি টাইপ সিস্টেমটি একটি এফএসএম তৈরি করতে ব্যবহার করছি Builderযা আপনাকে একটি তৈরি করতে দেয় যা উভয়কে একটি BuilderWithEngineবা একটিতে পরিণত করা যেতে পারে BuilderWithTransmission, যা উভয়ই পরে পরিণত হতে পারে CompleteBuilderযা কার্যকর করেbuild()পদ্ধতি। Buildচ্ছিক সেটটারগুলির মধ্যে এইগুলির মধ্যে যে কোনও একটিতে অনুরোধ করা যেতে পারে। এখানে চিত্র বর্ণনা লিখুন

public interface Car {
    public Engine getEngine(); // required
    public Transmission getTransmission(); // required
    public Stereo getStereo(); // optional

    public class Builder extends OptionalBuilder {
        public BuilderWithEngine engine(Engine engine) {
            return new BuilderWithEngine(engine, stereo);
        }
        public BuilderWithTransmission transmission(Transmission transmission) {
            return new BuilderWithTransmission(transmission, stereo);
        }
        @Override
        public Builder stereo(Stereo stereo) {
            super.stereo(stereo);
            return this;
        }
    }

    public class OptionalBuilder {
        protected Stereo stereo = null;
        private OptionalBuilder() {}
        public OptionalBuilder stereo(Stereo stereo) {
            this.stereo = stereo;
            return this;
        }
    }

    public class BuilderWithEngine extends OptionalBuilder {
        private Engine engine;
        private BuilderWithEngine(Engine engine, Stereo stereo) {
            this.engine = engine;
            this.stereo = stereo;
        }
        public CompleteBuilder transmission(Transmission transmission) {
            return new CompleteBuilder(engine, transmission, stereo);
        }
        public BuilderWithEngine engine(Engine engine) {
            this.engine = engine;
            return this;
        }
        @Override
        public BuilderWithEngine stereo(Stereo stereo) {
            super.stereo(stereo);
            return this;
        }
    }

    public class BuilderWithTransmission extends OptionalBuilder {
        private Transmission transmission;
        private BuilderWithTransmission(Transmission transmission, Stereo stereo) {
            this.transmission = transmission;
            this.stereo = stereo;
        }
        public CompleteBuilder engine(Engine engine) {
            return new CompleteBuilder(engine, transmission, stereo);
        }
        public BuilderWithTransmission transmission(Transmission transmission) {
            this.transmission = transmission;
            return this;
        }
        @Override
        public BuilderWithTransmission stereo(Stereo stereo) {
            super.stereo(stereo);
            return this;
        }
    }

    public class CompleteBuilder extends OptionalBuilder {
        private Engine engine;
        private Transmission transmission;
        private CompleteBuilder(Engine engine, Transmission transmission, Stereo stereo) {
            this.engine = engine;
            this.transmission = transmission;
            this.stereo = stereo;
        }
        public CompleteBuilder engine(Engine engine) {
            this.engine = engine;
            return this;
        }
        public CompleteBuilder transmission(Transmission transmission) {
            this.transmission = transmission;
            return this;
        }
        @Override
        public CompleteBuilder stereo(Stereo stereo) {
            super.stereo(stereo);
            return this;
        }
        public Car build() {
            return new Car() {
                @Override
                public Engine getEngine() {
                    return engine;
                }
                @Override
                public Transmission getTransmission() {
                    return transmission;
                }
                @Override
                public Stereo getStereo() {
                    return stereo;
                }
            };
        }
    }
}

আপনি বলতে পারেন যে এটি ভাল স্কেল করে না, কারণ প্রয়োজনীয় বিভিন্ন বিল্ডার শ্রেণীর সংখ্যা হবে ও (2 ^ n) যেখানে এন বাধ্যতামূলক সেটারের সংখ্যা।

সুতরাং আমার প্রশ্ন: এটি আরও মার্জিতভাবে করা যেতে পারে?

(আমি জাভা নিয়ে কাজ করে এমন একটি উত্তর খুঁজছি, যদিও স্কালাটি গ্রহণযোগ্য হবে)


1
এই সমস্ত নির্ভরতা দাঁড়াতে আইওসি ধারকটি ব্যবহার করা থেকে আপনাকে কী বাধা দেয়? এছাড়াও, আমি পরিষ্কার করছি না কেন, যদি আপনি দৃ have়ভাবে বলেছেন যে আদেশ যদি কিছু যায় আসে না, আপনি কেবল ফিরে আসা সাধারণ সেটার পদ্ধতি ব্যবহার করতে পারেন না this?
রবার্ট হার্ভে

.engine(e)একজন নির্মাতার কাছে দু'বার প্রার্থনা করার অর্থ কী ?
এরিক tদ

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

1
রবার্ট: লক্ষ্যটি হ'ল টাইপ চেকার এই সত্যটি প্রয়োগ করে যে ইঞ্জিন এবং সংক্রমণ উভয়ই বাধ্যতামূলক; আপনি কল build()না করে engine(e)এবং transmission(t)আগে না ডাকলে এইভাবে আপনি কল করতে পারবেন না ।
ডেরাববিংক

এরিক: আপনি একটি ডিফল্ট Engineবাস্তবায়ন দিয়ে শুরু করতে এবং পরে আরও নির্দিষ্ট প্রয়োগের মাধ্যমে এটি ওভাররাইট করতে পারেন। তবে সম্ভবত এটি engine(e)সেটার না হয়ে একটি সংযোজনকারী না হলে এটি আরও অর্থবোধ করবে addEngine(e)। এটি এমন এক Carনির্মাতার পক্ষে কার্যকর হবে যা একাধিক ইঞ্জিন / মোটর সহ হাইব্রিড গাড়ি উত্পাদন করতে পারে। যেহেতু এটি একটি স্বীকৃত উদাহরণ, আমি কেন ব্রিফিংয়ের জন্য আপনি এটি করতে চাইতে পারেন সে সম্পর্কে বিশদ বিবরণে যাইনি।
7:30

উত্তর:


3

আপনার সরবরাহিত পদ্ধতিটির ভিত্তিতে আপনার দুটি পৃথক প্রয়োজনীয়তা রয়েছে বলে মনে হচ্ছে।

  1. কেবলমাত্র একটি (প্রয়োজনীয়) ইঞ্জিন, কেবল একটি (প্রয়োজনীয়) সংক্রমণ, এবং কেবলমাত্র একটি (alচ্ছিক) স্টেরিও।
  2. এক বা একাধিক (প্রয়োজনীয়) ইঞ্জিন, এক বা একাধিক (প্রয়োজনীয়) সংক্রমণ এবং এক বা একাধিক (optionচ্ছিক) স্টেরিও।

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

একটি গাড়ীতে কেবল একটি ইঞ্জিন এবং একটি সংক্রমণ থাকতে পারে। এমনকি হাইব্রিড গাড়িগুলিতে কেবল একটি ইঞ্জিন রয়েছে (সম্ভবত এ GasAndElectricEngine)

আমি উভয় বাস্তবায়ন সম্বোধন করব:

public class CarBuilder {

    public CarBuilder(Engine engine, Transmission transmission) {
        // ...
    }

    public CarBuilder setStereo(Stereo stereo) {
        // ...
        return this;
    }
}

এবং

public class CarBuilder {

    public CarBuilder(List<Engine> engines, List<Transmission> transmission) {
        // ...
    }

    public CarBuilder addStereo(Stereo stereo) {
        // ...
        return this;
    }
}

যদি কোনও ইঞ্জিন এবং সংক্রমণ দরকার হয় তবে সেগুলি কনস্ট্রাক্টরে থাকা উচিত।

আপনি যদি জানেন না কোন ইঞ্জিন বা সংক্রমণ প্রয়োজন, তবে এখনও একটি সেট করবেন না; এটি এমন একটি লক্ষণ যা আপনি বিল্ডারকে স্ট্যাকের অনেক দূরে তৈরি করছেন।


2

নাল অবজেক্ট প্যাটার্নটি কেন ব্যবহার হচ্ছে না? এই বিল্ডার থেকে মুক্তি পান, আপনি যে সবচেয়ে মার্জিত কোডটি লিখতে পারেন তা হ'ল আপনাকে আসলে লিখতে হবে না।

public final class CarImpl implements Car {
    private final Engine engine;
    private final Transmission transmission;
    private final Stereo stereo;

    public CarImpl(Engine engine, Transmission transmission) {
        this(engine, transmission, new DefaultStereo());
    }

    public CarImpl(Engine engine, Transmission transmission, Stereo stereo) {
        this.engine = engine;
        this.transmission = transmission;
        this.stereo = stereo;
    }

    //...

}

এই আমি সরাসরি চিন্তা। যদিও আমার কাছে তিনটি প্যারামিটার কনস্ট্রাক্টর নেই, কেবল দুটি প্যারামিটার কনস্ট্রাক্টরের যে বাধ্যতামূলক উপাদান রয়েছে এবং তারপরে স্টিরিওর জন্য সেটার এটি itচ্ছিক হিসাবে রয়েছে।
এনকেইটার

1
একটি সরল (স্বীকৃত) উদাহরণে Carযেমন এটি বোঝা যাবে কারণ ক্যাটার যুক্তি সংখ্যা খুব কম very তবে, আপনি কিছুটা মাঝারি জটিল (> ​​= 4 বাধ্যতামূলক যুক্তি) নিয়ে কাজ করার সাথে সাথে পুরো জিনিসটি হ্যান্ডেল করা / কম পঠনযোগ্য হয়ে উঠবে ("ইঞ্জিন বা সংক্রমণটি আগে এসেছিল?")। এজন্য আপনি কোনও বিল্ডার ব্যবহার করবেন: আপনি কী তৈরি করছেন তা API আপনাকে আরও সুস্পষ্ট হতে বাধ্য করে।
14

1
@derabbink এই ক্ষেত্রে আপনার ক্লাসটি আরও ছোট করে কেন ভাঙছেন না? কোনও বিল্ডার ব্যবহার করে ক্লাসটি খুব বেশি কাজ করছে এবং তা অভাবনীয় হয়ে উঠেছে তা কেবল লুকিয়ে রাখবে।
চিতল

1
প্যাটার্ন উন্মাদনা শেষ করার জন্য কুডোস।
রবার্ট হার্ভে

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

1

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

যাইহোক, যদি মন্তব্যগুলিতে আপনার সীমাবদ্ধতা থাকে তবে আপনাকে ইঞ্জিন এবং ট্রান্সমিশন করতে হবে, তবে বাধ্যতামূলক সমস্ত সম্পত্তি রেখে এটি প্রয়োগ করুন বিল্ডারের নির্মাণকারী's

new Builder(e, t).build();                      // ok
new Builder(e, t).stereo(s).build();            // ok
new Builder(e, t).stereo(s).stereo(s).build();  // exception on second call to stereo as stereo is already set 

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


0

প্রয়োজনীয় বিভিন্ন বিল্ডার শ্রেণীর সংখ্যা হবে ও (2 ^ n) যেখানে এন বাধ্যতামূলক সেটারের সংখ্যা।

আপনি ইতিমধ্যে এই প্রশ্নের সঠিক দিক অনুমান করেছেন।

আপনি যদি সংকলন-সময় পরীক্ষা করতে চান তবে আপনার (2^n)প্রকারের প্রয়োজন হবে । আপনি যদি রান-টাইম চেকিং পেতে চান তবে আপনার একটি পরিবর্তনশীল প্রয়োজন যা (2^n)স্টেটগুলি সঞ্চয় করতে পারে ; একটি nবিট পূর্ণসংখ্যা করতে হবে।


কারণ সি ++ সমর্থন অ-টাইপ টেমপ্লেট প্যারামিটার (মান পূর্ণসংখ্যা যেমন) , এটিও সম্ভব হতে একটি সি ++ বর্গ টেমপ্লেটের জন্য মধ্যে instantiated করা O(2^n)বিভিন্ন ধরনের, একটি স্কিম অনুরূপ ব্যবহার এই

তবে যে ভাষাগুলিতে নন-টাইপ টেম্পলেট প্যারামিটারগুলি সমর্থন করে না, আপনি O(2^n)বিভিন্ন প্রকারের ইনস্ট্যান্ট করতে টাইপ সিস্টেমের উপর নির্ভর করতে পারবেন না ।


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


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

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