संपादित करें: मैं यह बताना चाहता हूं कि यह प्रश्न एक सैद्धांतिक समस्या का वर्णन करता है, और मुझे पता है कि मैं अनिवार्य पैरामीटर के लिए कंस्ट्रक्टर तर्क का उपयोग कर सकता हूं, या एपीआई गलत तरीके से उपयोग किए जाने पर रनटाइम अपवाद को फेंक सकता हूं। हालाँकि, मैं एक ऐसे समाधान की तलाश कर रहा हूँ जिसमें निर्माण तर्क या रनटाइम जाँच की आवश्यकता न हो ।
कल्पना कीजिए कि आपके पास Car
इस तरह का एक इंटरफ़ेस है:
public interface Car {
public Engine getEngine(); // required
public Transmission getTransmission(); // required
public Stereo getStereo(); // optional
}
जैसा कि टिप्पणी से पता चलता है, एक Car
होना चाहिए Engine
और Transmission
एक Stereo
वैकल्पिक है। इसका मतलब है कि एक बिल्डर जो 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)
, और अंत में वैकल्पिक 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();
हालांकि, ऐसे बहुत सारे परिदृश्य हैं जिनमें यह बिल्डर के उपयोगकर्ता के लिए आदर्श नहीं है, खासकर यदि बिल्डर ने न केवल बसेरा किया है, बल्कि इसके अलावा, या यदि उपयोगकर्ता उस आदेश को नियंत्रित नहीं कर सकता है जिसमें बिल्डर के लिए कुछ गुण उपलब्ध हो जाएंगे।
इसका एकमात्र उपाय जो मैं सोच सकता था, वह बहुत ही जटिल है: अनिवार्य गुणों के हर संयोजन के लिए सेट होने या अभी तक सेट नहीं होने के कारण, मैंने एक समर्पित बिल्डर वर्ग बनाया है जो जानता है कि संभावित अन्य अनिवार्य निवासियों को आने से पहले बुलाया जाना चाहिए राज्य जहां build()
विधि उपलब्ध होनी चाहिए, और उन बसने वालों में से प्रत्येक अधिक पूर्ण प्रकार के बिल्डर को लौटाता है जो एक build()
विधि से युक्त एक कदम है ।
मैंने नीचे कोड जोड़ा है, लेकिन आप कह सकते हैं कि मैं FSM बनाने के लिए टाइप सिस्टम का उपयोग कर रहा हूं जो आपको एक बनाने की अनुमति देता है Builder
, जिसे BuilderWithEngine
या तो एक में बदल दिया जा सकता है BuilderWithTransmission
, जिसे फिर दोनों को एक में बदल दिया जा सकता है CompleteBuilder
, जो लागू होता है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 ^ एन) होगी जहां एन अनिवार्य बसने वालों की संख्या है।
इसलिए मेरा प्रश्न: क्या यह अधिक शान से किया जा सकता है?
(मैं एक जवाब की तलाश कर रहा हूं जो जावा के साथ काम करता है, हालांकि स्काला स्वीकार्य होगा)
.engine(e)
एक बिल्डर के लिए दो बार आह्वान करने का क्या मतलब है ?
build()
यदि आपने कॉल नहीं किया है engine(e)
और transmission(t)
इससे पहले।
Engine
कार्यान्वयन के साथ शुरू करना चाहते हैं , और बाद में इसे अधिक विशिष्ट कार्यान्वयन के साथ अधिलेखित कर सकते हैं। लेकिन सबसे अधिक संभावना है कि यह अधिक समझ में आता है अगर engine(e)
एक सेटर नहीं था, लेकिन एक योजक addEngine(e)
:। यह एक Car
बिल्डर के लिए उपयोगी होगा जो एक से अधिक इंजन / मोटर के साथ हाइब्रिड कारों का उत्पादन कर सकता है। चूंकि यह एक आकस्मिक उदाहरण है, इसलिए मैं इस बात पर नहीं गया कि आप ऐसा क्यों करना चाहते हैं - संक्षिप्तता के लिए।
this
?