संक्षेप में, अपने सॉफ़्टवेयर को पुन: प्रयोज्यता के लिए इंजीनियर न करें क्योंकि कोई अंतिम उपयोगकर्ता परवाह नहीं करता है यदि आपके कार्यों का पुन: उपयोग किया जा सकता है। इसके बजाय, डिजाइन की समझ के लिए इंजीनियर - क्या मेरा कोड किसी और के लिए आसान है या मेरे भविष्य को समझने में स्वयं को भूल गया है? - और डिजाइन लचीलापन- जब मुझे अनिवार्य रूप से बग्स को ठीक करना होगा, सुविधाओं को जोड़ना होगा, या अन्यथा कार्यक्षमता को संशोधित करना होगा, तो मेरा कोड परिवर्तनों का विरोध कैसे करेगा? आपके ग्राहक की एकमात्र बात यह है कि जब वह बग की रिपोर्ट करता है या बदलाव के लिए पूछता है तो आप कितनी जल्दी प्रतिक्रिया दे सकते हैं। अपने डिजाइन के बारे में ये प्रश्न पूछना संयोग से कोड में परिणाम है कि पुन: प्रयोज्य है, लेकिन यह दृष्टिकोण आपको उस कोड के जीवन पर आने वाली वास्तविक समस्याओं से बचने पर ध्यान केंद्रित करता है ताकि आप उदात्त, अव्यवहारिक का पीछा करने के बजाय अंत उपयोगकर्ता की बेहतर सेवा कर सकें। गर्दन-दाढ़ी को खुश करने के लिए "इंजीनियरिंग" आदर्श।
आपके द्वारा दिए गए उदाहरण के रूप में सरल रूप में कुछ के लिए, आपका प्रारंभिक कार्यान्वयन ठीक है क्योंकि यह कितना छोटा है, लेकिन यह सीधा डिजाइन समझना और भंगुर हो जाएगा यदि आप बहुत अधिक कार्यात्मक लचीलेपन को जाम करने की कोशिश करते हैं (जैसा कि डिजाइन लचीलेपन के विपरीत)। एक प्रक्रिया। नीचे समझ और लचीलेपन के लिए जटिल प्रणालियों को डिजाइन करने के लिए मेरे पसंदीदा दृष्टिकोण की मेरी व्याख्या है जो मुझे आशा है कि मैं उनके द्वारा क्या मतलब दिखाऊंगा। मैं इस रणनीति को किसी ऐसी चीज के लिए नियोजित नहीं करूंगा, जो एक ही प्रक्रिया में 20 से कम लाइनों में लिखी जा सकती है क्योंकि ऐसा कुछ बहुत पहले से ही समझ और लचीलापन के लिए मेरे मानदंड को पूरा करता है।
वस्तुएं, प्रक्रियाएं नहीं
पुराने स्कूलों के मॉड्यूल जैसे वर्गों का उपयोग करने के बजाय, जिन रूटीनों के लिए आप अपने सॉफ्टवेयर को करना चाहिए, उन चीजों को निष्पादित करने के लिए कॉल करते हैं, डोमेन को ऑब्जेक्ट के रूप में मॉडलिंग करने पर विचार करें, जो हाथ में कार्य को पूरा करने के लिए बातचीत और सहयोग करते हैं। ऑब्जेक्ट-ओरिएंटेड प्रतिमान में विधियां मूल रूप से वस्तुओं के बीच सिग्नल होने के लिए बनाई गई थीं ताकि वह अपनी चीज को Object1
बता सके Object2
, जो कुछ भी हो, और संभवतः रिटर्न सिग्नल प्राप्त हो। इसका कारण यह है कि ऑब्जेक्ट-ओरिएंटेड प्रतिमान स्वाभाविक रूप से अपने डोमेन ऑब्जेक्ट्स और उनके इंटरैक्शन को मॉडलिंग करने के बारे में है न कि एक समान तरीके से इंपीरियल प्रतिमान के पुराने कार्यों और प्रक्रियाओं को व्यवस्थित करने के लिए। के मामले मेंvoid destroyBaghdad
उदाहरण, बगदाद या किसी भी अन्य चीज़ के विनाश को संभालने के लिए एक संदर्भ-कम सामान्य विधि लिखने की कोशिश करने के बजाय (जो जल्दी से जटिल, समझने में कठिन और भंगुर हो सकता है), नष्ट होने वाली हर चीज को समझने के लिए जिम्मेदार होना चाहिए कि कैसे खुद को नष्ट करने के लिए। उदाहरण के लिए, आपके पास एक इंटरफ़ेस है जो उन चीजों के व्यवहार का वर्णन करता है जिन्हें नष्ट किया जा सकता है:
interface Destroyable {
void destroy();
}
फिर आपके पास एक शहर है जो इस इंटरफ़ेस को लागू करता है:
class City implements Destroyable {
@Override
public void destroy() {
...code that destroys the city
}
}
कुछ भी नहीं है कि एक आवृत्ति के विनाश के लिए कहता है कि City
कैसे होता है कभी परवाह है, तो उस कोड के बाहर कहीं भी मौजूद करने के लिए कोई कारण नहीं है City::destroy
, और वास्तव में, City
खुद के बाहर के आंतरिक कामकाज का अंतरंग ज्ञान तंग युग्मन होगा जो कम कर देता है felxibility चूंकि आपको उन बाहरी तत्वों पर विचार करना है, जिनके लिए आपको कभी भी व्यवहार को संशोधित करने की आवश्यकता होगी City
। इनकैप्सुलेशन के पीछे यही सही उद्देश्य है। इसे ऐसे समझें कि हर वस्तु का अपना एपीआई होता है, जिससे आपको अपनी जरूरत के मुताबिक कुछ भी करने में सक्षम होना चाहिए ताकि आप अपने अनुरोधों को पूरा करने के बारे में चिंता कर सकें।
प्रतिनिधिमंडल, "नियंत्रण" नहीं
अब, चाहे आपका कार्यान्वयन वर्ग है City
या इस Baghdad
बात पर निर्भर करता है कि शहर को नष्ट करने की प्रक्रिया कितनी सामान्य है। सभी संभाव्यता में, एक City
छोटे टुकड़ों से बना होगा जिसे शहर के कुल विनाश को पूरा करने के लिए व्यक्तिगत रूप से नष्ट करने की आवश्यकता होगी, इसलिए उस स्थिति में, उन टुकड़ों में से प्रत्येक को भी लागू किया जाएगा Destroyable
, और उन्हें City
नष्ट करने के लिए प्रत्येक को निर्देश दिया जाएगा। खुद को उसी तरह से बाहर से किसी ने City
खुद को नष्ट करने का अनुरोध किया ।
interface Part extends Destroyable {
...part-specific methods
}
class Building implements Part {
...part-specific methods
@Override
public void destroy() {
...code to destroy a building
}
}
class Street implements Part {
...part-specific methods
@Override
public void destroy() {
...code to destroy a building
}
}
class City implements Destroyable {
public List<Part> parts() {...}
@Override
public void destroy() {
parts().forEach(Destroyable::destroy);
}
}
यदि आप वास्तव में पागल हो जाना चाहते हैं और उस विचार को लागू करना चाहते हैं Bomb
जो एक स्थान पर गिरा दिया गया है और एक निश्चित दायरे में सब कुछ नष्ट कर देता है, तो यह कुछ इस तरह दिख सकता है:
class Bomb {
private final Integer radius;
public Bomb(final Integer radius) {
this.radius = radius;
}
public void drop(final Grid grid, final Coordinate target) {
new ObjectsByRadius(
grid,
target,
this.radius
).forEach(Destroyable::destroy);
}
}
ObjectsByRadius
Bomb
इनपुट्स से गणना की गई वस्तुओं का एक समूह का प्रतिनिधित्व Bomb
करता है क्योंकि इस बात की परवाह नहीं करता है कि गणना कितनी लंबी है, क्योंकि यह वस्तुओं के साथ काम कर सकता है। यह पुन: प्रयोज्य रूप से संयोगवश होता है, लेकिन मुख्य लक्ष्य यह है कि गणना को छोड़ने Bomb
और वस्तुओं को नष्ट करने की प्रक्रियाओं से अलग किया जाए ताकि आप प्रत्येक टुकड़े को समझ सकें और कैसे वे एक साथ फिट होते हैं और पूरे एल्गोरिथ्म को फिर से व्यवस्थित किए बिना एक व्यक्तिगत टुकड़े के व्यवहार को बदल सकते हैं। ।
सहभागिता, एल्गोरिदम नहीं
एक जटिल एल्गोरिथ्म के लिए मापदंडों की सही संख्या का अनुमान लगाने की कोशिश करने के बजाय, यह प्रक्रिया को मॉडल करने के लिए और अधिक अर्थ देता है बातचीत की वस्तुओं के एक सेट के रूप में, प्रत्येक अत्यंत संकीर्ण भूमिकाओं के साथ, क्योंकि यह आपको अपनी जटिलता को मॉडल करने की क्षमता देगा। इन अच्छी तरह से परिभाषित, समझने में आसान और लगभग अपरिवर्तित वस्तुओं के बीच बातचीत के माध्यम से प्रक्रिया। जब सही ढंग से किया जाता है, तो यह कुछ सबसे जटिल संशोधनों को भी तुच्छ बना देता है जैसा कि एक इंटरफ़ेस या दो को लागू करना और जो आपके ऑब्जेक्ट को तुरंत लागू करते हैं main()
।
मैं आपको अपने मूल उदाहरण के लिए कुछ देता हूँ, लेकिन मैं ईमानदारी से यह नहीं समझ सकता कि इसका "प्रिंट ... डे लाइट सेविंग" का क्या मतलब है। समस्या की उस श्रेणी के बारे में मैं क्या कह सकता हूं कि किसी भी समय आप एक गणना कर रहे हैं, जिसके परिणाम स्वरूप कई तरीके तैयार किए जा सकते हैं, मेरा पसंदीदा तरीका यह है कि इस तरह से नीचे गिरा जाए:
interface Result {
String print();
}
class Caclulation {
private final Parameter paramater1;
private final Parameter parameter2;
public Calculation(final Parameter parameter1, final Parameter parameter2) {
this.parameter1 = parameter1;
this.parameter2 = parameter2;
}
public Result calculate() {
...calculate the result
}
}
class FormattedResult {
private final Result result;
public FormattedResult(final Result result) {
this.result = result;
}
@Override
public String print() {
...interact with this.result to format it and return the formatted String
}
}
चूंकि आपका उदाहरण जावा लाइब्रेरी से कक्षाओं का उपयोग करता है जो इस डिजाइन का समर्थन नहीं करते हैं, आप बस ZonedDateTime
सीधे एपीआई का उपयोग कर सकते हैं। यहाँ विचार यह है कि प्रत्येक गणना अपनी ही वस्तु के भीतर कूटबद्ध है। यह इस बारे में कोई धारणा नहीं बनाता है कि इसे कितनी बार चलाना चाहिए या परिणाम को कैसे प्रारूपित करना चाहिए। यह विशेष रूप से गणना का सबसे सरल रूप प्रदर्शन करने से संबंधित है। इससे इसे समझना और बदलना दोनों आसान हो जाता है। इसी तरह, Result
विशेष रूप से गणना के परिणाम को एनकैप्सुलेट करने से संबंधित है, और FormattedResult
विशेष रूप से Result
हमारे द्वारा परिभाषित नियमों के अनुसार इसे प्रारूपित करने के लिए बातचीत करने के साथ संबंधित है । इस तरह,हम अपने प्रत्येक तरीके के लिए सही तर्क पा सकते हैं क्योंकि उनमें से प्रत्येक में एक अच्छी तरह से परिभाषित कार्य है । जब तक इंटरफेस नहीं बदलते (जो वे अपनी वस्तुओं की जिम्मेदारियों को ठीक से कम से कम करने की संभावना नहीं रखते हैं) तब तक आगे बढ़ना संशोधित करना बहुत सरल है। हमारीmain()
विधि इस तरह दिख सकती है:
class App {
public static void main(String[] args) {
final List<Set<Paramater>> parameters = ...instantiated from args
parameters.forEach(set -> {
System.out.println(
new FormattedResult(
new Calculation(
set.get(0),
set.get(1)
).calculate()
).print()
);
});
}
}
तथ्य की बात के रूप में, ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग को विशेष रूप से इंपीरियल प्रतिमान की जटिलता / लचीलेपन की समस्या के समाधान के रूप में आविष्कार किया गया था क्योंकि वहाँ कोई अच्छा जवाब नहीं है (कि हर कोई स्वतंत्र रूप से किसी भी तरह सहमत हो सकता है या पहुंच सकता है) मुहावरे के भीतर शाही कार्यों और प्रक्रियाओं को निर्दिष्ट करें।