اصول S.O.L.I.D در کاتلین

1- اصل single responsibility (مسئولیت واحد)
اصل single responsibility بیان می کند که هر class باید تنها یک مسئولیت داشته باشد.
data class User( var id:Long, var name:String, var password:String ){ Fun signIn(){ //this method will do signing in operation } Fun signOut(){ // this method will do signing out operation } } }
data class User( var id:Long, var name:String, var password:String ) Class AuthentivationService(){ Fun signIn(){ //this method will do signing in operation } Fun signOut(){ // this method will do signing out operation } }
اگر
حالا فرض کنیم که ما باید در متدهای sign-in و sign-out ، فرایند authentication تعدادی تغییر ایجاد کنیم (شکل 1) ، در این صورت کلاس user ما تحت تأثیر قرار می گیرد.
آنگاه
برای حل مسئله باید کلاس جدیدی (شکل 2) ایجاد کنیم تا فرآیند authentication را مدیریت کرده و متدهای sign-in و sign-out را در آن کلاس move کنیم.
2- اصل open-closed
open: این بدان معناست که ما می توانیم feature های جدیدی را به کلاس های خود اضافه کنیم.
closed: این بدان معناست که feature های اصلی کلاس نباید تغییر کند.
class Rectangle{ private var length private var height //getters / setters … } class Circle{ private var radius //getters / setters … } class AreaFactory{ public fun calculateArea(shapes:ArrayList<Object>):Double{ var area = 0 for (shape in shapes){ if(shape is Rectangle) { var rect = shape as Rectangle area += (rect.getLength()*rect.getHeight()) } else if (shape is Circle){ var circle = shape as Circle area += (circle.getRadius()*circle. getRadius ()*Math.PI) } else{ throw new RunTimeExeption(“shape not supported”) } } return area } }
Public interface Shape { fun getArea() } class Rectangle : Shape { private var length private var height //getters / setters … override fun getArea():Double { return (length*height) } class Circle: Shape { private var radius //getters / setters … override fun getArea():Double{ return (radius * radius *Math.PI) } class AreaFactory{ fun calculateArea(shapes:ArrayList<String>):Double{ var area:Double = 0.toDouble() for (shape in shapes){ area += shape.getArea() } return area } }
اگر
در شکل 1 می بینیم که یک دستور if else برای جدا کردن shape ها داریم ، و با افزایش shape ها ، آنها نیز به همان ترتیب به رشد خود ادامه می دهند ، زیرا کلاس برای modification (اصلاح) بسته نشده است ، یا برای extention (گسترش) نیز باز نیست.
آنگاه
ما با کمک interface این مشکل را برطرف می کنیم. در صورت نیاز به افزودن shape های بیشتر ، کلاس area factory به هیچ تغییری نیاز ندارد زیرا این کلاس از طریق اینترفیس shape ، برای توسعه باز است.
3- اصل جایگزینی liskov
کلاس فرزند (child) باید جایگزین کلاس پدر (parent) شود.
abstract class Vehicle { protected var isEngineWorking = false abstract fun startEngine() abstract fun stopEngine() abstract fun moveForward() abstract fun moveBack() } class Car:Vehicle() { override fun startEngine() { println("Engine started") isEngineWorking = true } override fun stopEngine() { println("Engine stopped") isFngineWorking = false } override fun moveForward() { println("Moving forward") } override fun moveBack() { println("Moving back!") } } class Bicycle:Vehicle(){ override fun startEngine() { TODO("Not yet implemented") } override fun stopEngine() { TODO("Not yet implemented") } override fun moveForward() { println("Moving forward") } override fun moveBack() { println("Moving back!") } }
interface Vehicle { fun move forward() fun moveBack } abstract class VehicleWithEngine:Vehicle { private var isEngineWorking = false open fun startEngine(){isEngineWorking = true} open fun stopEngine(){isEngineWorking = false) } class Car:VehicleWithEngine(){ override fun startEngine() { super.startEngine() println("Engine started") } override fun stopEngine() { super.stopEngine() println("Engine stopped") } override fun moveForward() { println("Moving forward") } override fun moveBack() { println("Moving back!") } } class Bicycle:Vehicle{ override fun moveForward() { println("Moving forward") } override fun moveBack() { println("Moving back!") } }
اگر
در شکل 1 هنگام ایجاد کلاس دوچرخه ، متدهای startEngine و stopEngine بی فایده خواهند بود زیرا دوچرخه موتور ندارد.
آنگاه
برای رفع این حالت ، ما می توانیم یک کلاس فرزند (child) جدید ایجاد کنیم (شکل 2) که از Vehicle ، extend می شود. این کلاس دارای موتور خواهد بود
4- اصل Interface segregation (تفکیک Interface)
این اصل بیان می کند که آن دسته از interface هایی که بسیار شلوغ و بزرگ(دارای متدهای اضافه) هستند ، باید به interface های کوچکتر تقسیم شود.
interface Animal { fun eat() fun sleep() fun fly() } class Cat : Animal { override fun eat() { println("Cat is eating fish") } override fun sleep() { println("Cat is sleeping on its owner's bed") } override fun fly() { TODO ( "Not yet implemented") //cat can not fly!! } } class Bird : Animal { override fun eat() { println("Bird is eating forage") } override fun sleep() { println("Bird is sleeping in the nest") } override fun fly(){ println("Bird is flying in so high" } }
interface Animal { fun eat() fun sleep() } interface FlyingAnimal { fun fly() } class Cat : Animal { override fun eat() { println("Cat is eating fish") } override fun sleep() { println("Cat is sleeping on its owner's bed") } } class Bird : Animal, FlyingAnimal { override fun eat() { println("Bird is eating forage") } override fun sleep() { println("Bird is sleeping in the nest") } override fun fly() { println("Bird is flying in so high") }
اگر
همانطور که در شکل 1 مشاهده می کنید برخی از حیوانات مانند گربه ها نمی توانند پرواز کنند . در حالی که implement و اجرای متد fly در آن ضروری است.
آنگاه
برای رفع این مشکل ، یک interface جدید (شکل 2) برای حیوانات پرنده ایجاد می کنیم و متد fly را از اینترفیس animalحذف می کنیم
5- اصل Dependency Inversion (وارونگی وابستگی)
این اصل بیان می کند که ماژول های سطح بالا نباید به ماژول های سطح پایین وابسته باشند.
class AndroidDeveloper { fun developerMobileApp(){ println("developing Android Application by using kotlin") } } class IosDeveloper { fun developerMobileApp(){ println("developing iOS Application by using swift") } } fun main(){ val androidDeveloper = AndroidDeveloper() val iosDeveloper = IosDeveloper() androidDeveloper.developerMobileApp() iosDeveloper.developerMobileApp() }
interface MobileDeveloper{ fun developerMobileApp() } class AndroidDeveloper (var mobileService: MobileServices): MobileDeveloper { override fun developerMobileApp(){ println("developing Android Application by using kotlin." + "application will work with ${mobileService.serviceName}") } enum class MobileServices (var serviceName: String) { HMS( serviceName: "Huawei Mobile Service"), GMSC serviceName: "Google Mobile Service"), BOTH( ServiceName: "Huawei Mobile Service and Google Mobile Service") } } class IosDeveloper : MobileDeveloper { override fun developerMobileApp({ println("developing iOS Application by using swift") } } fun main{ val developers = arrayListof (AndroidDeveloper (AndroidDeveloper. MobileServices.HMS) , IosDeveloper()) developers.forEach { developer -> developer.developerMobileApp() } }
اگر
بیایید فرض کنیم که ما باید یک اپلیکیشن را برای Android و ios در شکل 1 توسعه دهیم.
آنگاه
برای رفع مشکل ، می توانیم یک interface (شکل 2) ایجاد کنیم و کلاس های AndroidDeveloper و IosDeveloper این interface را implement (پیاده سازی) خواهند کرد.
دیدگاهتان را بنویسید