Computer >> कंप्यूटर >  >> प्रणाली >> Android

ठोस सिद्धांतों का परिचय

क्रिप्टोफ़ोलियो ऐप सीरीज़ - भाग 1

सॉफ्टवेयर हमेशा परिवर्तन की स्थिति में होता है। प्रत्येक परिवर्तन का पूरी परियोजना पर नकारात्मक प्रभाव पड़ सकता है। इसलिए ज़रूरी है कि सभी नए बदलावों को लागू करते समय होने वाले नुकसान को रोका जाए।

"क्रिप्टोफोलियो" (पहले "माई क्रिप्टो कॉइन्स") ऐप के साथ, मैं कदम दर कदम बहुत सारे नए कोड बनाऊंगा और मैं इसे एक अच्छे तरीके से करना शुरू करना चाहता हूं। मैं चाहता हूं कि मेरा प्रोजेक्ट ठोस गुणवत्ता वाला हो। सबसे पहले हमें आधुनिक सॉफ्टवेयर बनाने के मूल सिद्धांतों को समझने की जरूरत है। उन्हें ठोस सिद्धांत कहा जाता है। इतना आकर्षक नाम! ?

श्रृंखला सामग्री

  • परिचय:2018–2019 में एक आधुनिक Android ऐप बनाने का रोडमैप
  • भाग 1:ठोस सिद्धांतों का परिचय (आप यहां हैं)
  • भाग 2:अपना Android ऐप कैसे बनाना शुरू करें:मॉकअप, UI और XML लेआउट बनाना
  • भाग 3:उस आर्किटेक्चर के बारे में सब कुछ:विभिन्न आर्किटेक्चर पैटर्न की खोज करना और उन्हें अपने ऐप में कैसे उपयोग करना है
  • भाग 4:डैगर 2 के साथ अपने ऐप में डिपेंडेंसी इंजेक्शन कैसे लागू करें
  • भाग 5:Retrofit, OkHttp, Gson, Glide और Coroutines का उपयोग करके RESTful Web Services को हैंडल करें

सिद्धांतों का नारा

ठोस एक स्मरणीय परिवर्णी शब्द है। यह पांच बुनियादी वस्तु-उन्मुख डिजाइन सिद्धांतों को परिभाषित करने में मदद करता है:

  1. एस जिम्मेदारी का सिद्धांत
  2. कलम बंद सिद्धांत
  3. एल iskov प्रतिस्थापन सिद्धांत
  4. मैं इंटरफ़ेस अलगाव सिद्धांत
  5. डी एपेंडेंसी उलटा सिद्धांत

आगे हम उनमें से प्रत्येक पर व्यक्तिगत रूप से चर्चा करने जा रहे हैं। प्रत्येक के लिए, मैं खराब बनाम अच्छे कोड उदाहरण प्रदान करने जा रहा हूं। ये उदाहरण कोटलिन भाषा का उपयोग करके Android के लिए लिखे गए हैं।

एकल उत्तरदायित्व सिद्धांत

एक वर्ग की केवल एक ही जिम्मेदारी होनी चाहिए।

ऐप द्वारा प्रदान की जाने वाली कार्यक्षमता के एक हिस्से के लिए प्रत्येक वर्ग या मॉड्यूल जिम्मेदार होना चाहिए। इसलिए जब वह किसी एक चीज़ को संभालता है, तो उसे बदलने का केवल एक ही मुख्य कारण होना चाहिए। यदि आपकी कक्षा या मॉड्यूल एक से अधिक कार्य करता है, तो आपको कार्यात्मकताओं को अलग-अलग में विभाजित करना चाहिए।

इस सिद्धांत को बेहतर ढंग से समझने के लिए, मैं एक उदाहरण के रूप में स्विस सेना के चाकू को लूंगा। यह चाकू अपने मुख्य ब्लेड के अलावा कई कार्यों के लिए जाना जाता है। इसके अंदर अन्य उपकरण एकीकृत हैं, जैसे स्क्रूड्रिवर, एक कैन ओपनर, और कई अन्य।

आपके लिए यहां स्वाभाविक प्रश्न यह है कि मैं इस चाकू को एकल कार्यक्षमता के लिए एक उदाहरण के रूप में क्यों सुझा रहा हूं? लेकिन जरा सोचिए एक पल के लिए। इस चाकू की अन्य मुख्य विशेषता पॉकेट साइज होने के साथ-साथ गतिशीलता है। इसलिए भले ही यह कुछ अलग-अलग कार्य प्रदान करता हो, फिर भी यह अपने मुख्य उद्देश्य के लिए इतना छोटा होना चाहिए कि इसे आराम से अपने साथ ले जा सके।

वही नियम प्रोग्रामिंग के साथ जाते हैं। जब आप अपनी कक्षा या मॉड्यूल बनाते हैं, तो इसका कुछ मुख्य वैश्विक उद्देश्य होना चाहिए। उसी समय जब आप कार्यक्षमता को अलग करके सब कुछ बहुत अधिक सरल बनाने की कोशिश कर रहे हों तो आप ओवरप्ले नहीं कर सकते। तो याद रखें, संतुलन बनाए रखें।

ठोस सिद्धांतों का परिचय

एक क्लासिक उदाहरण अक्सर इस्तेमाल किया जाने वाला तरीका हो सकता है onBindViewHolder RecyclerView विजेट एडॉप्टर बनाते समय।

? खराब कोड उदाहरण:

class MusicVinylRecordRecyclerViewAdapter(private val vinyls: List<VinylRecord>, private val itemLayout: Int) 
 : RecyclerView.Adapter<MusicVinylRecordRecyclerViewAdapter.ViewHolder>() {
    ...
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val vinyl = vinyls[position]
        holder.itemView.tag = vinyl

        holder.title!!.text = vinyl.title
        holder.author!!.text = vinyl.author
        holder.releaseYear!!.text = vinyl.releaseYear
        holder.country!!.text = vinyl.country
        holder.condition!!.text = vinyl.condition

        /**
         *  Here method violates the Single Responsibility Principle!!!
         *  Despite its main and only responsibility to be adapting a VinylRecord object
         *  to its view representation, it is also performing data formatting as well.
         *  It has multiple reasons to be changed in the future, which is wrong.
         */

        var genreStr = ""
        for (genre in vinyl.genres!!) {
            genreStr += genre + ", "
        }
        genreStr = if (genreStr.isNotEmpty())
            genreStr.substring(0, genreStr.length - 2)
        else
            genreStr

        holder.genre!!.text = genreStr
    }
    ...
}

? अच्छा कोड उदाहरण:

class MusicVinylRecordRecyclerViewAdapter(private val vinyls: List<VinylRecord>, private val itemLayout: Int) 
 : RecyclerView.Adapter<MusicVinylRecordRecyclerViewAdapter.ViewHolder>() {
    ...
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val vinyl = vinyls[position]
        holder.itemView.tag = vinyl

        holder.title!!.text = vinyl.title
        holder.author!!.text = vinyl.author
        holder.releaseYear!!.text = vinyl.releaseYear
        holder.country!!.text = vinyl.country
        holder.condition!!.text = vinyl.condition
        
        /**
         * Instead of performing data formatting operations here, we move that responsibility to
         * other class. Actually here you see only direct call of top-level function
         * convertArrayListToString - new Kotlin language feature. However don't be mistaken,
         * because Kotlin compiler behind the scenes still is going to create a Java class, and
         * than the individual top-level functions will be converted to static methods. So single
         * responsibility for each class.
         */

        holder.genre!!.text =  convertArrayListToString(vinyl.genres)
    }
    ...
}

एकल उत्तरदायित्व सिद्धांत को ध्यान में रखते हुए विशेष रूप से तैयार किया गया कोड उन अन्य सिद्धांतों के करीब होगा जिन पर हम चर्चा करने जा रहे हैं।

खुले-बंद सिद्धांत

सॉफ़्टवेयर इकाइयां विस्तार के लिए खुली होनी चाहिए, लेकिन संशोधन के लिए बंद होनी चाहिए।

यह सिद्धांत बताता है कि जब आप सभी सॉफ़्टवेयर भागों जैसे क्लास, मॉड्यूल और फ़ंक्शंस लिखते हैं, तो आपको उन्हें विस्तार के लिए खुला रखना चाहिए लेकिन किसी भी संशोधन के लिए बंद कर देना चाहिए। इसका क्या मतलब है?

मान लीजिए कि हम एक मजदूर वर्ग बनाते हैं। अगर हमें नई कार्यक्षमता जोड़ने या कुछ बदलाव करने की आवश्यकता है तो उस वर्ग को ट्विक करने की कोई आवश्यकता नहीं होनी चाहिए। इसके बजाय हमें उस वर्ग का नया उपवर्ग बनाकर उस वर्ग का विस्तार करने में सक्षम होना चाहिए जहाँ हम आसानी से सभी नई आवश्यक सुविधाएँ जोड़ सकें। सुविधाओं को हमेशा इस तरह से पैरामीटर किया जाना चाहिए कि एक उपवर्ग ओवरराइड कर सके।

आइए एक उदाहरण देखें जहां हम एक विशेष FeedbackManager . बनाएंगे उपयोगकर्ता के लिए एक अलग प्रकार का कस्टम संदेश दिखाने के लिए कक्षा।

? खराब कोड उदाहरण:

class MainActivity : AppCompatActivity() {

    lateinit var feedbackManager: FeedbackManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        feedbackManager = FeedbackManager(findViewById(android.R.id.content));
    }

    override fun onStart() {
        super.onStart()

        feedbackManager.showToast(CustomToast())
    }
}

class FeedbackManager(var view: View) {

    // Imagine that we need to add new type feedback message. What would happen?
    // We would need to modify this manager class. But to follow Open Closed Principle we
    // need to write a code that can be adapted automatically to the new requirements without
    // rewriting the old classes.

    fun showToast(customToast: CustomToast) {
        Toast.makeText(view.context, customToast.welcomeText, customToast.welcomeDuration).show()
    }

    fun showSnackbar(customSnackbar: CustomSnackbar) {
        Snackbar.make(view, customSnackbar.goodbyeText, customSnackbar.goodbyeDuration).show()
    }
}

class CustomToast {

    var welcomeText: String = "Hello, this is toast message!"
    var welcomeDuration: Int = Toast.LENGTH_SHORT
}

class CustomSnackbar {

    var goodbyeText: String = "Goodbye, this is snackbar message.."
    var goodbyeDuration: Int = Toast.LENGTH_LONG
}

? अच्छा कोड उदाहरण:

class MainActivity : AppCompatActivity() {

    lateinit var feedbackManager: FeedbackManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        feedbackManager = FeedbackManager(findViewById(android.R.id.content));
    }

    override fun onStart() {
        super.onStart()

        feedbackManager.showSpecialMessage(CustomToast())
    }
}

class FeedbackManager(var view: View) {

    // Again the same situation - we need to add new type feedback message. We have to write code
    // that can be adapted to new requirements without changing the old class implementation.
    // Here the solution is to focus on extending the functionality by using interfaces and it
    // follows the Open Closed Principle.

    fun showSpecialMessage(message: Message) {
        message.showMessage(view)
    }
}

interface Message {
    fun showMessage(view: View)
}

class CustomToast: Message {

    var welcomeText: String = "Hello, this is toast message!"
    var welcomeDuration: Int = Toast.LENGTH_SHORT

    override fun showMessage(view: View) {
        Toast.makeText(view.context, welcomeText, welcomeDuration).show()
    }
}

class CustomSnackbar: Message {

    var goodbyeText: String = "Goodbye, this is snackbar message.."
    var goodbyeDuration: Int = Toast.LENGTH_LONG

    override fun showMessage(view: View) {
        Snackbar.make(view, goodbyeText, goodbyeDuration).show()
    }
}

खुला-बंद सिद्धांत अगले दो सिद्धांतों के लक्ष्यों को संक्षेप में प्रस्तुत करता है जिनके बारे में मैं नीचे बात करता हूं। तो चलिए उन पर चलते हैं।

लिस्कोव प्रतिस्थापन सिद्धांत

कार्यक्रम में वस्तुओं को उस कार्यक्रम की शुद्धता को बदले बिना उनके उपप्रकारों के उदाहरणों से बदला जा सकता है।

इस सिद्धांत का नाम एक कुशल कंप्यूटर वैज्ञानिक बारबरा लिस्कोव के नाम पर रखा गया है। इस सिद्धांत का सामान्य विचार यह है कि कार्यक्रम के व्यवहार को बदले बिना वस्तुओं को उनके उपप्रकारों के उदाहरणों से बदला जा सकता है।

मान लें कि आपके ऐप में आपके पास MainClass . है जो BaseClass . पर निर्भर करता है , जो SubClass . का विस्तार करता है . संक्षेप में, इस सिद्धांत का पालन करने के लिए, आपका MainClass जब आप BaseClass . को बदलने का निर्णय लेते हैं तो कोड और आपका ऐप सामान्य रूप से बिना किसी समस्या के काम करना चाहिए उदाहरण के लिए SubClass उदाहरण।

ठोस सिद्धांतों का परिचय

इस सिद्धांत को और भी बेहतर ढंग से समझने के लिए, मैं आपको Square . के साथ एक शास्त्रीय, समझने में आसान उदाहरण देता हूं और Rectangle विरासत।

? खराब कोड उदाहरण:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val rectangleFirst: Rectangle = Rectangle()
        rectangleFirst.width = 2
        rectangleFirst.height = 3

        textViewRectangleFirst.text = rectangleFirst.area().toString()
        // The result of the first rectangle area is 6, which is correct as 2 x 3 = 6.

        // The Liskov Substitution Principle states that a subclass (Square) should override
        // the parent class (Rectangle) in a way that does not break functionality from a
        // consumers’s point of view. Let's see.
        val rectangleSecond: Rectangle = Square()
        // The user assumes that it is a rectangle and try to set the width and the height as usual
        rectangleSecond.width = 2
        rectangleSecond.height = 3

        textViewRectangleSecond.text = rectangleSecond.area().toString()
        // The expected result of the second rectangle should be 6 again, but instead it is 9.
        // So as you see this object oriented approach for Square extending Rectangle is wrong.
    }
}

open class Rectangle {

    open var width: Int = 0
    open var height: Int = 0

    open fun area(): Int {
        return width * height
    }
}

class Square : Rectangle() {

    override var width: Int
        get() = super.width
        set(width) {
            super.width = width
            super.height = width
        }

    override var height: Int
        get() = super.height
        set(height) {
            super.width = height
            super.height = height
        }
}

? अच्छा कोड उदाहरण:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Here it is presented a way how to organize these Rectangle and Square classes better to
        // meet the Liskov Substitution Principle. No more unexpected result.
        val rectangleFirst: Shape = Rectangle(2,3)
        val rectangleSecond: Shape = Square(3)

        textViewRectangleFirst.text = rectangleFirst.area().toString()
        textViewRectangleSecond.text = rectangleSecond.area().toString()
    }
}

class Rectangle(var width: Int, var height: Int) : Shape() {

    override fun area(): Int {
        return width * height
    }
}

class Square(var edge: Int) : Shape() {

    override fun area(): Int {
        return edge * edge
    }
}

abstract class Shape {
    abstract fun area(): Int
}

अपने पदानुक्रम को लिखने से पहले हमेशा सोचें। जैसा कि आप इस उदाहरण में देख सकते हैं, वास्तविक जीवन की वस्तुएं हमेशा समान OOP वर्गों के लिए मैप नहीं करती हैं। आपको एक अलग दृष्टिकोण खोजने की जरूरत है।

इंटरफ़ेस पृथक्करण सिद्धांत

कई क्लाइंट-विशिष्ट इंटरफ़ेस एक सामान्य-उद्देश्य इंटरफ़ेस से बेहतर हैं।

यहां तक ​​​​कि नाम भी जटिल लगता है, लेकिन सिद्धांत को समझना आसान है। इसमें कहा गया है कि क्लाइंट को कभी भी विधियों पर निर्भर रहने या इंटरफ़ेस को लागू करने के लिए मजबूर नहीं किया जाना चाहिए जिसका वह उपयोग नहीं करता है। एक वर्ग को कम से कम विधियों और विशेषताओं के लिए डिज़ाइन करने की आवश्यकता है। इंटरफ़ेस बनाते समय इसे बहुत बड़ा न बनाने का प्रयास करें। इसके बजाय इसे छोटे इंटरफ़ेस में विभाजित करें ताकि इंटरफ़ेस के क्लाइंट को केवल उन तरीकों के बारे में पता चले जो प्रासंगिक हैं।

इस सिद्धांत का विचार पाने के लिए मैंने बटरफ्लाई और ह्यूमनॉइड रोबोट के साथ फिर से खराब बनाम अच्छे कोड उदाहरण बनाए हैं। ?

ठोस सिद्धांतों का परिचय

? खराब कोड उदाहरण:

/**
 * Let's imagine we are creating some undefined robot. We decide to create an interface with all
 * possible functions to it.
 */
interface Robot {
    fun giveName(newName: String)
    fun reset()
    fun fly()
    fun talk()
}

/**
 * First we are creating butterfly robot which implements that interface.
 */
class ButterflyRobot : Robot {
    var name: String = ""

    override fun giveName(newName: String) {
        name = newName
    }

    override fun reset() {
        // Calls reset command for the robot. Any robot's software should be possible to reset.
        // That is reasonable and we will implement this.
        TODO("not implemented")
    }

    override fun fly() {
        // Calls fly command for the robot. This is specific functionality of our butterfly robot.
        // We will definitely implement this.
        TODO("not implemented")
    }

    override fun talk() {
        // Calls talk command for the robot.
        // WRONG!!! Our butterfly robot is not going to talk, just fly! Why we need implement this?
        // Here it is a violation of Interface Segregation Principle as we are forced to implement
        // a method that we are not going to use.
        TODO("???")
    }
}

/**
 * Next we are creating humanoid robot which should be able to do similar actions as human and it
 * also implements same interface.
 */
class HumanoidRobot : Robot {
    var name: String = ""

    override fun giveName(newName: String) {
        name = newName
    }

    override fun reset() {
        // Calls reset command for the robot. Any robot's software should be possible to reset.
        // That is reasonable and we will implement this.
        TODO("not implemented")
    }

    override fun fly() {
        // Calls fly command for the robot.
        // That the problem! We have never had any intentions for our humanoid robot to fly.
        // Here it is a violation of Interface Segregation Principle as we are forced to implement
        // a method that we are not going to use.
        TODO("???")
    }

    override fun talk() {
        // Calls talk command for the robot. This is specific functionality of our humanoid robot.
        // We will definitely implement this.
        TODO("not implemented")
    }
}

? अच्छा कोड उदाहरण:

/**
 * Let's imagine we are creating some undefined robot. We should create a generic interface with all
 * possible functions common to all types of robots.
 */
interface Robot {
    fun giveName(newName: String)
    fun reset()
}

/**
 * Specific robots which can fly should have their own interface defined.
 */
interface Flyable {
    fun fly()
}

/**
 * Specific robots which can talk should have their own interface defined.
 */
interface Talkable {
    fun talk()
}

/**
 * First we are creating butterfly robot which implements a generic interface and a specific one.
 * As you see we are not required anymore to implement functions which are not related to our robot!
 */
class ButterflyRobot : Robot, Flyable {
    var name: String = ""

    override fun giveName(newName: String) {
        name = newName
    }

    override fun reset() {
        // Calls reset command for the robot. Any robot's software should be possible to reset.
        // That is reasonable and we will implement this.
        TODO("not implemented")
    }

    // Calls fly command for the robot. This is specific functionality of our butterfly robot.
    // We will definitely implement this.
    override fun fly() {
        TODO("not implemented")
    }
}

/**
 * Next we are creating humanoid robot which should be able to do similar actions as human and it
 * also implements generic interface and specific one for it's type.
 * As you see we are not required anymore to implement functions which are not related to our robot!
 */
class HumanoidRobot : Robot, Talkable {
    var name: String = ""

    override fun giveName(newName: String) {
        name = newName
    }

    override fun reset() {
        // Calls reset command for the robot. Any robot's software should be possible to reset.
        // That is reasonable and we will implement this.
        TODO("not implemented")
    }

    override fun talk() {
        // Calls talk command for the robot. This is specific functionality of our humanoid robot.
        // We will definitely implement this.
        TODO("not implemented")
    }
}

निर्भरता उलटा सिद्धांत

एक व्यक्ति को "अवक्षेपों पर निर्भर होना चाहिए, [नहीं] अंशों पर।"

अंतिम सिद्धांत कहता है कि उच्च-स्तरीय मॉड्यूल निम्न-स्तरीय मॉड्यूल पर निर्भर नहीं होने चाहिए। दोनों को अमूर्तन पर निर्भर होना चाहिए। सार विवरण पर निर्भर नहीं होना चाहिए। विवरण अमूर्त पर निर्भर होना चाहिए।

सिद्धांत का मुख्य विचार मॉड्यूल और कक्षाओं के बीच प्रत्यक्ष निर्भरता नहीं है। इसके बजाय उन्हें अबास्ट्रक्शन (जैसे इंटरफेस) पर निर्भर बनाने की कोशिश करें।

इसे और भी सरल बनाने के लिए, यदि आप किसी अन्य वर्ग के अंदर एक वर्ग का उपयोग करते हैं, तो यह वर्ग इंजेक्शन वाले वर्ग पर निर्भर होगा। यह सिद्धांत के विचार का उल्लंघन करता है और आपको ऐसा नहीं करना चाहिए। आपको सभी वर्गों को अलग करने का प्रयास करना चाहिए।

? खराब कोड उदाहरण:

class Radiator {
    var temperatureCelsius : Int = 0

    fun turnOnHeating(newTemperatureCelsius : Int) {
        temperatureCelsius  = newTemperatureCelsius
        // To turn on heating for the radiator we will have to do specific steps for this device.
        // Radiator will have it's own technical procedure of how it will be turned on.
        // Procedure implemented here.
        TODO("not implemented")
    }
}

class AirConditioner {
    var temperatureFahrenheit: Int = 0

    fun turnOnHeating(newTemperatureFahrenheit: Int) {
        temperatureFahrenheit = newTemperatureFahrenheit
        // To turn on heating for air conditioner we will have to do some specific steps
        // just for this device, as air conditioner will have it's own technical procedure.
        // This procedure is different compared to radiator and will be implemented here.
        TODO("not implemented")
    }
}

class SmartHome {

    // To our smart home control system we added a radiator control.
    var radiator: Radiator = Radiator()
    // But what will be if later we decide to change our radiator to air conditioner instead?
    // var airConditioner: AirConditioner = AirConditioner()
    // This SmartHome class is dependent of the class Radiator and violates Dependency Inversion Principle.

    var recommendedTemperatureCelsius : Int = 20

    fun warmUpRoom() {
        radiator.turnOnHeating(recommendedTemperatureCelsius)
        // If we decide to ignore the principle there may occur some important mistakes, like this
        // one. Here we pass recommended temperature in celsius but our air conditioner expects to
        // get it in Fahrenheit.
        // airConditioner.turnOnHeating(recommendedTemperatureCelsius)
    }
}

? अच्छा कोड उदाहरण:

// First let's create an abstraction - interface.
interface Heating {
    fun turnOnHeating(newTemperatureCelsius : Int)
}

// Class should implement the Heating interface.
class Radiator : Heating {
    var temperatureCelsius : Int = 0

    override fun turnOnHeating(newTemperatureCelsius: Int) {
        temperatureCelsius  = newTemperatureCelsius
        // Here radiator will have it's own technical procedure implemented of how it will be turned on.
        TODO("not implemented")
    }
}

// Class should implement the Heating interface.
class AirConditioner : Heating {
    var temperatureFahrenheit: Int = 0

    override fun turnOnHeating(newTemperatureCelsius: Int) {
        temperatureFahrenheit = newTemperatureCelsius * 9/5 + 32
        // Air conditioner's turning on technical procedure will be implemented here.
        TODO("not implemented")
    }
}

class SmartHome {

    // To our smart home control system we added a radiator control.
    var radiator: Heating = Radiator()
    // Now we have an answer to the question what will be if later we decide to change our radiator
    // to air conditioner. Our class is going to depend on the interface instead of another
    // injected class.
    // var airConditioner: Heating = AirConditioner()

    var recommendedTemperatureCelsius : Int = 20

    fun warmUpRoom() {
        radiator.turnOnHeating(recommendedTemperatureCelsius)
        // As we depend on the common interface, there is no more chance for mistakes.
        // airConditioner.turnOnHeating(recommendedTemperatureCelsius)
    }
}

संक्षेप में संक्षेप करने के लिए

यदि हम इन सभी सिद्धांतों के बारे में सोचें, तो हम देख सकते हैं कि वे एक दूसरे के पूरक हैं। ठोस सिद्धांतों का पालन करने से हमें कई लाभ होंगे। वे हमारे ऐप को पुन:प्रयोज्य, रखरखाव योग्य, स्केलेबल, परीक्षण योग्य बना देंगे।

बेशक, इन सभी सिद्धांतों का पूरी तरह से पालन करना हमेशा संभव नहीं होता है, क्योंकि कोड लिखते समय सब कुछ व्यक्तिगत स्थितियों पर निर्भर करता है। लेकिन एक डेवलपर के रूप में आपको कम से कम उन्हें जानना चाहिए ताकि आप तय कर सकें कि उन्हें कब लागू करना है।

रिपॉजिटरी

यह पहला भाग है जहाँ हम नया कोड लिखने के बजाय सीखते हैं और अपने प्रोजेक्ट की योजना बनाते हैं। यहां पार्ट 1 ब्रांच कमिट का लिंक दिया गया है, जो मूल रूप से प्रोजेक्ट का "हैलो वर्ल्ड" प्रारंभिक कोड है।

GitHub पर स्रोत देखें

मुझे उम्मीद है कि मैं सॉलिड सिद्धांतों को अच्छी तरह समझाने में कामयाब रहा हूं। नीचे टिप्पणी करने के लिए स्वतंत्र महसूस करें।

आशी! पढ़ने के लिए धन्यवाद! मैंने मूल रूप से इस पोस्ट को 23 फरवरी, 2018 को अपने निजी ब्लॉग www.baruckis.com के लिए प्रकाशित किया था।


  1. मैकबुक प्रो के लिए सर्वश्रेष्ठ 4k मॉनिटर का परिचय

    क्या आपने 4K मॉनिटर के बारे में सुना है? सबसे अधिक संभावना है, आपने पहले ही इसके बारे में सुना है। आखिरकार, यह इन दिनों काफी लोकप्रिय है। इसकी अच्छी बात यह है कि आप इसे अपने मैकबुक के साथ इस्तेमाल कर सकते हैं। आप अपने मैकबुक को मिरर करने के लिए 4K मॉनिटर का उपयोग कर सकते हैं या आप इसे सेकेंडरी डिस्

  1. Google का प्रोजेक्ट Fi:कॉलिंग के भविष्य का परिचय

    वायरलेस नेटवर्क का बाजार अत्यधिक भीड़भाड़ वाला है और फिर भी हमें कोई बड़ा सौदा नहीं मिल रहा है। वास्तव में, सेल फोन वाहकों को एक खराब प्रतिष्ठा मिली है और जरूरी नहीं कि गलत कारणों से ही हो। ऐसा अनलिमिटेड डेटा प्लान, डेटा की बढ़ती कीमतों और घटते डेटा कैप से संबंधित विवादों के कारण है। डेटा योजनाओं पर

  1. LibreOffice 7.1 समीक्षा - अनिश्चितता का सिद्धांत

    वे कहते हैं कि लोग जैसे-जैसे बड़े होते जाते हैं, वैसे-वैसे उनमें कड़वाहट बढ़ती जाती है। मैं कहता हूं, यह उम्र का कार्य नहीं है, यह अनुभव का कार्य है। आशा सीमित है, और जैसे-जैसे कोई जीवन से गुजरता है और बार-बार निराशा का फल चखता है, वह मिट जाती है और छिटक जाती है। लेकिन मरने के लिए उम्मीद आखिरी चीज ह