बिल्डर पैटर्न का उपयोग करने के कुछ सामान्य , वास्तविक दुनिया उदाहरण क्या हैं ? यह आपको क्या खरीदता है? सिर्फ फ़ैक्टरी पैटर्न का उपयोग क्यों न करें?
बिल्डर पैटर्न का उपयोग करने के कुछ सामान्य , वास्तविक दुनिया उदाहरण क्या हैं ? यह आपको क्या खरीदता है? सिर्फ फ़ैक्टरी पैटर्न का उपयोग क्यों न करें?
जवाबों:
एक बिल्डर और कारखाने IMHO के बीच महत्वपूर्ण अंतर यह है कि एक बिल्डर तब उपयोगी होता है जब आपको ऑब्जेक्ट बनाने के लिए बहुत सारी चीजें करने की आवश्यकता होती है। उदाहरण के लिए एक DOM की कल्पना करो। आपको अपनी अंतिम वस्तु प्राप्त करने के लिए बहुत सारे नोड्स और विशेषताओं का निर्माण करना होगा। एक फैक्ट्री का उपयोग तब किया जाता है जब फैक्ट्री आसानी से पूरी विधि को एक विधि कॉल के भीतर बना सकती है।
एक बिल्डर का उपयोग करने का एक उदाहरण एक XML दस्तावेज़ है, मैंने इस मॉडल का उपयोग तब किया है जब HTML अंशों का निर्माण किया जाता है। उदाहरण के लिए, मेरे पास एक विशिष्ट प्रकार की तालिका बनाने के लिए एक बिल्डर हो सकता है और इसमें निम्न विधियाँ हो सकती हैं (पैरामीटर नहीं दिखाए गए हैं) :
BuildOrderHeaderRow()
BuildLineItemSubHeaderRow()
BuildOrderRow()
BuildLineItemSubRow()
यह बिल्डर तब मेरे लिए HTML बाहर थूक देगा। यह एक बड़ी प्रक्रियात्मक विधि के माध्यम से चलने की तुलना में पढ़ना बहुत आसान है।
विकिपीडिया पर बिल्डर पैटर्न देखें ।
नीचे जावा में पैटर्न और उदाहरण कोड के उपयोग के कुछ तर्क दिए गए हैं, लेकिन यह डिज़ाइन पैटर्न में गैंग ऑफ़ फोर द्वारा कवर किए गए बिल्डर पैटर्न का कार्यान्वयन है । जिन कारणों से आप इसका जावा में उपयोग करेंगे, वे अन्य प्रोग्रामिंग भाषाओं पर भी लागू होंगे।
जैसा कि जोशुआ बलोच ने प्रभावी जावा, द्वितीय संस्करण में बताया है :
बिल्डर पैटर्न उन वर्गों को डिजाइन करते समय एक अच्छा विकल्प है जिनके निर्माणकर्ता या स्थिर कारखाने मुट्ठी भर मापदंडों से अधिक होंगे।
हम सभी किसी न किसी बिंदु पर निर्माणकर्ताओं की एक सूची के साथ एक वर्ग का सामना कर रहे हैं जहां प्रत्येक जोड़ एक नया विकल्प पैरामीटर जोड़ता है:
Pizza(int size) { ... }
Pizza(int size, boolean cheese) { ... }
Pizza(int size, boolean cheese, boolean pepperoni) { ... }
Pizza(int size, boolean cheese, boolean pepperoni, boolean bacon) { ... }
इसे टेलिस्कोपिंग कंस्ट्रक्टर पैटर्न कहा जाता है। इस पैटर्न के साथ समस्या यह है कि एक बार निर्माणकर्ता 4 या 5 पैरामीटर के होते हैं, तो पैरामीटर के आवश्यक आदेश को याद रखना मुश्किल हो जाता है और साथ ही किसी विशेष निर्माता को आप किसी भी स्थिति में क्या चाहते हो सकते हैं।
टेलिस्कोपिंग कंस्ट्रक्टर पैटर्न के लिए आपके पास एक विकल्प जावाबैन पैटर्न है जहां आप एक निर्माता को अनिवार्य मापदंडों के साथ कॉल करते हैं और उसके बाद किसी भी वैकल्पिक सेटर को कॉल करते हैं:
Pizza pizza = new Pizza(12);
pizza.setCheese(true);
pizza.setPepperoni(true);
pizza.setBacon(true);
यहां समस्या यह है कि क्योंकि वस्तु कई कॉल पर बनाई गई है, जो इसके निर्माण के माध्यम से असंगत स्थिति में हो सकती है।थ्रेड सुरक्षा सुनिश्चित करने के लिए भी बहुत सारे अतिरिक्त प्रयासों की आवश्यकता होती है।
बेहतर विकल्प बिल्डर पैटर्न का उपयोग करना है।
public class Pizza {
private int size;
private boolean cheese;
private boolean pepperoni;
private boolean bacon;
public static class Builder {
//required
private final int size;
//optional
private boolean cheese = false;
private boolean pepperoni = false;
private boolean bacon = false;
public Builder(int size) {
this.size = size;
}
public Builder cheese(boolean value) {
cheese = value;
return this;
}
public Builder pepperoni(boolean value) {
pepperoni = value;
return this;
}
public Builder bacon(boolean value) {
bacon = value;
return this;
}
public Pizza build() {
return new Pizza(this);
}
}
private Pizza(Builder builder) {
size = builder.size;
cheese = builder.cheese;
pepperoni = builder.pepperoni;
bacon = builder.bacon;
}
}
ध्यान दें कि पिज्जा अपरिवर्तनीय है और पैरामीटर मान सभी एक ही स्थान पर हैं । क्योंकि बिल्डर की सेटर विधियां बिल्डर ऑब्जेक्ट को वापस करती हैं जो वे जंजीर करने में सक्षम हैं ।
Pizza pizza = new Pizza.Builder(12)
.cheese(true)
.pepperoni(true)
.bacon(true)
.build();
इस कोड में यह परिणाम है कि लिखना आसान है और पढ़ने और समझने में बहुत आसान है। इस उदाहरण में, बिल्डर विधि को पिज्जा ऑब्जेक्ट से कॉपी करने के बाद पैरामीटर की जांच करने के लिए बिल्ड विधि को संशोधित किया जा सकता है और अवैध पैरामीटर मान की आपूर्ति होने पर IllegalStateException को फेंक दें। यह पैटर्न लचीला है और भविष्य में इसके लिए अधिक मापदंडों को जोड़ना आसान है। यह वास्तव में केवल उपयोगी है यदि आप एक निर्माता के लिए 4 या 5 से अधिक पैरामीटर रखने जा रहे हैं। उस ने कहा, यह पहली जगह में सार्थक हो सकता है यदि आपको संदेह है कि आप भविष्य में अधिक पैरामीटर जोड़ सकते हैं।
मैंने इस विषय पर जोशुआ बलोच की किताब इफेक्टिव जावा, 2 डी एडिशन से बहुत अधिक उधार लिया है । इस पैटर्न और अन्य प्रभावी जावा प्रथाओं के बारे में अधिक जानने के लिए मैं इसकी अत्यधिक अनुशंसा करता हूं।
new Pizza.Builder(12).cheese().pepperoni().bacon().build();
Pizza.Builder(12).cheese().pepperoni().bacon().build();
आपको अपने कोड को फिर से जमा करने की आवश्यकता होगी या यदि आपको केवल कुछ पेपरोनी की आवश्यकता है पिज्जा। बहुत कम से कम आपको मूल रूप से सुझाए गए @Kamikaze Mercenary जैसे पैरामीट्रिक संस्करण भी प्रदान करने चाहिए। Pizza.Builder(12).cheese(true).pepperoni(false).bacon(false).build();
। तो फिर, हम कभी यूनिट-टेस्ट नहीं करते हैं, क्या हम?
एक रेस्तरां पर विचार करें। "आज का भोजन" का निर्माण एक कारखाना पैटर्न है, क्योंकि आप रसोई को बताते हैं "मुझे आज का भोजन मिलता है" और रसोई (कारखाना) यह तय करता है कि छिपी हुई कसौटियों के आधार पर किस वस्तु को उत्पन्न किया जाए।
यदि आप कस्टम पिज्जा ऑर्डर करते हैं तो बिल्डर दिखाई देता है। इस मामले में, वेटर शेफ (बिल्डर) से कहता है, "मुझे पिज्जा की ज़रूरत है; इसमें पनीर, प्याज और बेकन मिलाएं!" इस प्रकार, बिल्डर उन विशेषताओं को उजागर करता है जो उत्पन्न की गई वस्तु के पास होनी चाहिए, लेकिन उन्हें कैसे सेट करना है, यह छिपाता है।
.NET StringBuilder क्लास बिल्डर पैटर्न का एक बेहतरीन उदाहरण है। इसका उपयोग ज्यादातर चरणों की एक श्रृंखला बनाने के लिए किया जाता है। ToString () करने पर आपको मिलने वाला अंतिम परिणाम हमेशा एक स्ट्रिंग होता है, लेकिन StringBuilder वर्ग में जो कार्य किए जाते थे, उनके अनुसार उस स्ट्रिंग का निर्माण भिन्न होता है। योग करने के लिए, मूल विचार जटिल वस्तुओं का निर्माण करना है और इसे कैसे बनाया जा रहा है, इसके कार्यान्वयन के विवरण को छिपाना है।
b.append(...).append(...)
अंत में कॉल करने से पहले एक श्रृंखला हो सके toString()
। उद्धरण: infoq.com/articles/internal-dsls-java
बहु-थ्रेडेड समस्या के लिए, हमें प्रत्येक थ्रेड के लिए एक जटिल ऑब्जेक्ट की आवश्यकता होती है। ऑब्जेक्ट संसाधित किए जा रहे डेटा का प्रतिनिधित्व करता है, और उपयोगकर्ता इनपुट के आधार पर बदल सकता है।
क्या हम इसके बजाय एक कारखाने का उपयोग कर सकते हैं? हाँ
हम क्यों नहीं? बिल्डर मुझे लगता है कि अधिक समझ में आता है।
कारखानों का उपयोग विभिन्न प्रकार की वस्तुओं को बनाने के लिए किया जाता है जो एक ही मूल प्रकार हैं (समान इंटरफ़ेस या बेस क्लास को लागू करें)।
बिल्डर्स एक ही प्रकार की वस्तु का निर्माण बार-बार करते हैं, लेकिन निर्माण गतिशील है इसलिए इसे रनटाइम में बदला जा सकता है।
इससे निपटने के लिए आपके पास बहुत सारे विकल्प हैं। जॉक जैसी चीजों के बारे में सोचें:
m.expects(once())
.method("testMethod")
.with(eq(1), eq(2))
.returns("someResponse");
यह बहुत अधिक प्राकृतिक लगता है और संभव है।
इसमें xml बिल्डिंग, स्ट्रिंग बिल्डिंग और कई अन्य चीजें भी हैं। कल्पना कीजिए कि अगर java.util.Map
एक बिल्डर के रूप में रखा था। आप इस तरह से सामान कर सकते हैं:
Map<String, Integer> m = new HashMap<String, Integer>()
.put("a", 1)
.put("b", 2)
.put("c", 3);
Microsoft MVC फ्रेमवर्क से गुजरते समय, मुझे बिल्डर पैटर्न के बारे में एक विचार मिला। मैं कंट्रोलरबुअल श्रेणी में पैटर्न के पार आया। यह वर्ग नियंत्रक कारखाना वर्ग को लौटाने के लिए है, जिसे बाद में कंक्रीट नियंत्रक बनाने के लिए उपयोग किया जाता है।
बिल्डर पैटर्न का उपयोग करने में मुझे जो लाभ दिखाई देता है, वह यह है कि आप अपनी खुद की एक फैक्ट्री बना सकते हैं और उसे फ्रेमवर्क में प्लग कर सकते हैं।
@ टेटा, इटैलियन आदमी द्वारा चलाया जाने वाला एक रेस्तरां (फ्रेमवर्क) हो सकता है, जो पिज्जा परोसता है। पिज्जा तैयार करने के लिए इतालवी व्यक्ति (ऑब्जेक्ट बिल्डर) एक पिज्जा बेस (बेस क्लास) के साथ ओवेन (फैक्टरी) का उपयोग करता है।
अब भारतीय लड़का इतालवी आदमी से रेस्तरां लेता है। पिज्जा के बजाय भारतीय रेस्तरां (फ्रेमवर्क) सर्वर डोसा। डोसा तैयार करने के लिए भारतीय व्यक्ति (ऑब्जेक्ट बिल्डर) एक मैदा (बेस क्लास) के साथ फ्राइंग पैन (फैक्टरी) का उपयोग करता है
यदि आप परिदृश्य को देखते हैं, तो भोजन अलग है, जिस तरह से भोजन तैयार किया गया है वह अलग है, लेकिन एक ही रेस्तरां में (एक ही ढांचे के तहत)। रेस्तरां का निर्माण इस तरह से किया जाना चाहिए कि यह चीनी, मैक्सिकन या किसी भी भोजन का समर्थन कर सके। फ्रेम के अंदर ऑब्जेक्ट बिल्डर आप चाहते हैं की तरह तरह के प्लगइन की सुविधा। उदाहरण के लिए
class RestaurantObjectBuilder
{
IFactory _factory = new DefaultFoodFactory();
//This can be used when you want to plugin the
public void SetFoodFactory(IFactory customFactory)
{
_factory = customFactory;
}
public IFactory GetFoodFactory()
{
return _factory;
}
}
मैं हमेशा बिल्डर पैटर्न को नापसंद करता था, क्योंकि कुछ अनुभवी, बहुत कम अनुभव वाले प्रोग्रामर द्वारा दुर्व्यवहार किया जाता था। इसका एक पैटर्न जो केवल तभी समझ में आता है जब आपको कुछ डेटा से ऑब्जेक्ट को इकट्ठा करने की आवश्यकता होती है जिसे पोस्ट-इनिशियल स्टेप की आवश्यकता होती है (यानी एक बार जब सभी डेटा एकत्र हो जाता है - इसके साथ कुछ करें)। इसके बजाय, 99% समय बिल्डरों का उपयोग केवल वर्ग के सदस्यों को शुरू करने के लिए किया जाता है।
ऐसे मामलों में केवल घोषणा करना कहीं बेहतर है withXyz(...)
में वर्ग के अंदर और उन्हें अपने लिए एक संदर्भ लौटाना चाहिए।
इस पर विचार करो:
public class Complex {
private String first;
private String second;
private String third;
public String getFirst(){
return first;
}
public void setFirst(String first){
this.first=first;
}
...
public Complex withFirst(String first){
this.first=first;
return this;
}
public Complex withSecond(String second){
this.second=second;
return this;
}
public Complex withThird(String third){
this.third=third;
return this;
}
}
Complex complex = new Complex()
.withFirst("first value")
.withSecond("second value")
.withThird("third value");
अब हमारे पास एक साफ-सुथरा एकल वर्ग है जो अपनी खुद की इनिशियलाइज़ेशन का प्रबंधन करता है और बिल्डर की तरह ही बहुत काम करता है, सिवाय इसके कि वह कहीं अधिक सुरुचिपूर्ण है।
बिल्डर का एक और फायदा यह है कि अगर आपके पास कोई फैक्ट्री है, तो आपके कोड में अभी भी कुछ कपलिंग है, क्योंकि फैक्ट्री में काम करने के लिए, उसे उन सभी वस्तुओं को जानना होगा जो वह संभवतः बना सकता है। । यदि आप एक और ऑब्जेक्ट जोड़ते हैं जो बनाया जा सकता है, तो आपको उसे शामिल करने के लिए फ़ैक्टरी क्लास को संशोधित करना होगा। एब्सट्रैक्ट फैक्ट्री में भी ऐसा होता है।
बिल्डर के साथ, दूसरी ओर, आपको बस इस नए वर्ग के लिए एक नया कंक्रीट बिल्डर बनाना होगा। निर्देशक वर्ग वही रहेगा, क्योंकि उसे निर्माणकर्ता में बिल्डर प्राप्त होता है।
इसके अलावा, बिल्डर के कई स्वाद हैं। Kamikaze Mercenary`s एक और एक देता है।
/// <summary>
/// Builder
/// </summary>
public interface IWebRequestBuilder
{
IWebRequestBuilder BuildHost(string host);
IWebRequestBuilder BuildPort(int port);
IWebRequestBuilder BuildPath(string path);
IWebRequestBuilder BuildQuery(string query);
IWebRequestBuilder BuildScheme(string scheme);
IWebRequestBuilder BuildTimeout(int timeout);
WebRequest Build();
}
/// <summary>
/// ConcreteBuilder #1
/// </summary>
public class HttpWebRequestBuilder : IWebRequestBuilder
{
private string _host;
private string _path = string.Empty;
private string _query = string.Empty;
private string _scheme = "http";
private int _port = 80;
private int _timeout = -1;
public IWebRequestBuilder BuildHost(string host)
{
_host = host;
return this;
}
public IWebRequestBuilder BuildPort(int port)
{
_port = port;
return this;
}
public IWebRequestBuilder BuildPath(string path)
{
_path = path;
return this;
}
public IWebRequestBuilder BuildQuery(string query)
{
_query = query;
return this;
}
public IWebRequestBuilder BuildScheme(string scheme)
{
_scheme = scheme;
return this;
}
public IWebRequestBuilder BuildTimeout(int timeout)
{
_timeout = timeout;
return this;
}
protected virtual void BeforeBuild(HttpWebRequest httpWebRequest) {
}
public WebRequest Build()
{
var uri = _scheme + "://" + _host + ":" + _port + "/" + _path + "?" + _query;
var httpWebRequest = WebRequest.CreateHttp(uri);
httpWebRequest.Timeout = _timeout;
BeforeBuild(httpWebRequest);
return httpWebRequest;
}
}
/// <summary>
/// ConcreteBuilder #2
/// </summary>
public class ProxyHttpWebRequestBuilder : HttpWebRequestBuilder
{
private string _proxy = null;
public ProxyHttpWebRequestBuilder(string proxy)
{
_proxy = proxy;
}
protected override void BeforeBuild(HttpWebRequest httpWebRequest)
{
httpWebRequest.Proxy = new WebProxy(_proxy);
}
}
/// <summary>
/// Director
/// </summary>
public class SearchRequest
{
private IWebRequestBuilder _requestBuilder;
public SearchRequest(IWebRequestBuilder requestBuilder)
{
_requestBuilder = requestBuilder;
}
public WebRequest Construct(string searchQuery)
{
return _requestBuilder
.BuildHost("ajax.googleapis.com")
.BuildPort(80)
.BuildPath("ajax/services/search/web")
.BuildQuery("v=1.0&q=" + HttpUtility.UrlEncode(searchQuery))
.BuildScheme("http")
.BuildTimeout(-1)
.Build();
}
public string GetResults(string searchQuery) {
var request = Construct(searchQuery);
var resp = request.GetResponse();
using (StreamReader stream = new StreamReader(resp.GetResponseStream()))
{
return stream.ReadToEnd();
}
}
}
class Program
{
/// <summary>
/// Inside both requests the same SearchRequest.Construct(string) method is used.
/// But finally different HttpWebRequest objects are built.
/// </summary>
static void Main(string[] args)
{
var request1 = new SearchRequest(new HttpWebRequestBuilder());
var results1 = request1.GetResults("IBM");
Console.WriteLine(results1);
var request2 = new SearchRequest(new ProxyHttpWebRequestBuilder("localhost:80"));
var results2 = request2.GetResults("IBM");
Console.WriteLine(results2);
}
}
पिछले उत्तरों (वाक्य के अनुसार) का निर्माण, एक उत्कृष्ट वास्तविक दुनिया का उदाहरण है, जो ग्रूवी के समर्थन में बनाया गया है Builders
।
MarkupBuilder
StreamingMarkupBuilder
SwingXBuilder
ग्रूवी प्रलेखन में बिल्डर्स देखें
मैंने बिल्डर को घर-घर की मैसेजिंग लाइब्रेरी में इस्तेमाल किया। लाइब्रेरी कोर तार से डेटा प्राप्त कर रहा था, इसे बिल्डर उदाहरण के साथ इकट्ठा कर रहा था, फिर, एक बार बिल्डर ने फैसला किया कि उसे एक संदेश इंस्टेंस बनाने के लिए आवश्यक सब कुछ मिल गया है, बिल्डर। गेटमैसेज () एकत्र किए गए डेटा का उपयोग करके एक संदेश उदाहरण का निर्माण कर रहा था। तार।
इनरब्युट्रल, एक IntelliJ IDEA प्लगइन की जाँच करें जो जेनरेट मेनू (Alt + सम्मिलित) में एक 'बिल्डर' एक्शन जोड़ता है जो एक आंतरिक बिल्डर वर्ग उत्पन्न करता है जैसा कि प्रभावी जावा में वर्णित है।
जब मैं अपने XML के लिए मानक XMLGregorianCalendar का उपयोग करना चाहता था, तो मैंने जावा में डेटटाइम के मार्शलों को ऑब्जेक्ट करने के लिए कहा, मैंने इसका उपयोग करने के लिए कितना वजन और बोझिल था, इस पर बहुत सारी टिप्पणियां सुनीं। मैं एक्सएस में एक्सएमएल फ़ील्ड्स को कॉमरेट करने की कोशिश कर रहा था: टाइमज़ोन, मिलीसेकंड, आदि का प्रबंधन करने के लिए डेटाटाइम संरचना।
इसलिए मैंने एक ग्रेगोरियन कैलेन्डर या java.util.Date से XMLGregorian कैलेंडर बनाने के लिए एक उपयोगिता डिज़ाइन की।
क्योंकि मैं जहां काम करता हूं, मुझे इसे कानूनी के बिना ऑनलाइन साझा करने की अनुमति नहीं है, लेकिन यहां एक उदाहरण है कि एक ग्राहक इसका उपयोग कैसे करता है। यह विवरणों को अमूर्त करता है और XMLGregorianCalendar के कुछ कार्यान्वयन को फ़िल्टर करता है जो xs: डेटाइम के लिए कम उपयोग किए जाते हैं।
XMLGregorianCalendarBuilder builder = XMLGregorianCalendarBuilder.newInstance(jdkDate);
XMLGregorianCalendar xmlCalendar = builder.excludeMillis().excludeOffset().build();
यह पैटर्न एक फिल्टर का अधिक है क्योंकि यह xmlCalendar में फ़ील्ड्स को अपरिभाषित के रूप में सेट करता है, इसलिए उन्हें बाहर रखा गया है, यह अभी भी इसे "बनाता है"। मैंने आसानी से बिल्डर को एक्स: डेट, और एक्सएस: टाइम स्ट्रक्चर बनाने के लिए और जरूरत पड़ने पर टाइमजोन ऑफसेट्स में हेरफेर करने के लिए अन्य विकल्प जोड़े हैं।
यदि आपने कभी XMLGregorianCalendar को बनाने और उपयोग करने वाले कोड को देखा है, तो आप देखेंगे कि इसने हेरफेर करना कितना आसान बना दिया है।
एक महान वास्तविक दुनिया उदाहरण का उपयोग करना है जब इकाई आपकी कक्षाओं का परीक्षण कर रही है। आप sut (सिस्टम अंडर टेस्ट) बिल्डरों का उपयोग करते हैं।
उदाहरण:
वर्ग:
public class CustomAuthenticationService
{
private ICloudService _cloudService;
private IDatabaseService _databaseService;
public CustomAuthenticationService(ICloudService cloudService, IDatabaseService databaseService)
{
_cloudService = cloudService;
_databaseService = databaseService;
}
public bool IsAuthorized(User user)
{
//Implementation Details
return true;
}
परीक्षा:
[Test]
public void Given_a_User_With_Permission_When_Verifying_If_Authorized_Then_Authorize_It_Returning_True()
{
CustomAuthenticationService sut = new CustomAuthenticationServiceBuilder();
User userWithAuthorization = null;
var result = sut.IsAuthorized(userWithAuthorization);
Assert.That(result, Is.True);
}
sut बिल्डर:
public class CustomAuthenticationServiceBuilder
{
private ICloudService _cloudService;
private IDatabaseService _databaseService;
public CustomAuthenticationServiceBuilder()
{
_cloudService = new AwsService();
_databaseService = new SqlServerService();
}
public CustomAuthenticationServiceBuilder WithAzureService(AzureService azureService)
{
_cloudService = azureService;
return this;
}
public CustomAuthenticationServiceBuilder WithOracleService(OracleService oracleService)
{
_databaseService = oracleService;
return this;
}
public CustomAuthenticationService Build()
{
return new CustomAuthenticationService(_cloudService, _databaseService);
}
public static implicit operator CustomAuthenticationService (CustomAuthenticationServiceBuilder builder)
{
return builder.Build();
}
}
CustomAuthenticationService
कक्षा में बसने के बजाय इस मामले में एक बिल्डर की आवश्यकता क्यों होगी ?