
اساساً SOLIDیکی از مهمترین کلمات اختصاری در مفاهیم برنامه نویسی شی گرا است. استفاده از اصولSOLIDدر توسعهAndroidبرای رعایت اصولclean codeمی تواند مفید و موثر باشد. بنابراین اگر توسعه دهندگان اندروید کدهای خود را بدون استفاده از اصول طراحی ساختار یافته مانند اصولSOLIDطراحی و پیاده سازی کنند ، مشکلات طولانی مدت ایجاد می کنند و احتمال موفقیت یک برنامه در آینده کاهش می یابد. هدف این مقاله بحث در مورد اهمیت اصولSOLIDبرای توسعه دهندگان اندروید در طراحی و اجرای یک برنامه قوی است.
SOLIDچیست؟
SOLIDمخفف پنج اصل طراحی در توسعه نرم افزار شی گرا است که با هدف درک بهتر ، انعطاف پذیری و نگهداری بیشتر طرح های نرم افزاری انجام می شود. این پنج اصل توسطRobert C. Martin(عمو باب)شرح داده شده است. اصولSOLIDبه طور خلاصه به شرح زیر است:
- S- Single Responsibility Principle (SRP)
- Open/Closed Principle - O
- Liskov’s Substitution Principle - L
- Interface Segregation Principle - I
- Dependency Inversion Principle - D
چرا باید از اصولSOLIDدر توسعه نرم افزار استفاده کنیم؟
اگر توسعه دهندگان بدون استفاده از اصول طراحی ساختار یافته مانند اصولSOLIDکدهای خود را طراحی و اجرا کنند ، مشکلات طولانی مدت دیگری را برای توسعه دهندگان ایجاد می کنند که می خواهند در آینده روی پروژه کار کنند. این مسائل معمولاً بهSoftwareRotارجاع می شوند.
منظور ازSoftwareRotچیست؟
SoftwareRotیاCode RotیاSoftwareErosionبه تدریج باعث کاهش کیفیت نرم افزار در طول زمان یا کاهش پاسخگویی آن می شود که در نهایت منجر به نقص ، غیرقابل استفاده شدن نرم افزار و نیاز به ارتقا خواهد شد.
برخی علائم برای شناساییSoftwareRot
- Rigidity: تغییرات کوچک منجر به بازسازی کل نرم افزار می شود.
- Fragility: تغییر در یکcomponentباعث خطا یا نقص درcomponentدیگر می شود.
- Immobility: اگر از قسمتی از یکcomponentدر سیستم دیگری استفاده نشود ، این قسمتcomponentثابت(immobile)محسوب می شود.Immobilityمعمولاً به دلیل اتصال و وابستگی بین اجزای مختلف ایجاد می شود.
- Viscosity: هنگامی که اجرای آن دشوار است و اجرای آن نیز طولانی است.
Single Responsibility Principle(معروف بهSRP)
اصل Single Responsibility Principleنشان می دهد که هر کلاس باید یک و فقط یک مسئولیت داشته باشد. به عبارت دیگر اگر کلاس ما بیش از یک مسئولیت را انجام دهد باید عملکردها و مسئولیت ها را در کلاس های مختلف تقسیم کنیم. بنابراین این باعث می شود کلاس قدرت بیشتری داشته باشد.
به عنوان مثال در مثال زیر اگر کلاسUser شامل ویژگی ها و رفتارهای کلاس UserType باشد ، طراحی خوبی برای این کلاس نخواهد بود زیرا در این وضعیت کلاس بیش از یک مسئولیت دارد. بنابراین اگر بخواهیم مسئولیتی را تغییر دهیم احتمالاً باید مسئولیت های دیگر را نیز تغییر دهیم.
open class User {
var firstName: String? = null
var lastName: String? = null
}
class UserType : User() {
var selecting = 0
val isSpecialUser: Boolean
get() = selecting == 7
}
Open/Closed Principle
در مفاهیم شی گرا اصلOpen / Closedبه این معنی است: "entityهای نرم افزاری مانند کلاس ها,کامپوننت ها,ماژول ها باید برای توسعه باز باشند اما برای ویرایش بسته هستند". یعنی چنینentityمی تواند اجازه دهد رفتار آن بدون ویرایشsource codeخود توسعه یابد.
احتمالاً به نظر می رسد که این دو قسمت از اصلOpen / Closedمتناقض است اما اگر کلاس ها و وابستگی های آنها را به درستی و دقیق ساختار دهید می توانید بدون ویرایشsource codeموجود قابلیت های خود را اضافه کنید. به طور کلی ، شما با مراجعه بهAbstractionدر مفاهیم شی گرا برای وابستگی هایی مانندinterfaceها یاabstract classها به این اصل دست می یابید نه استفاده از کلاسconcrete.
نکته: کلاس هایconcreteدر برنامه نویسی شئ گرا کلاس هایی هستند که هیچ مفهوم انتزاعی (متدanstractیا عضوvirtual) در آنها وجود ندارد و تمام متد های آنها دارای پیاده سازی هستند و در نتیجه میتوان از آنها شی ساخت.
علاوه بر این می توان با ایجاد کلاس جدیدی کهinterfaceها را پیاده سازی می کند قابلیت ها را اضافه کرد. با استفاده از این روش می توانید خطر بروز اشکالات جدید در کد موجود را کاهش دهید و منجر به ساخت اپلیکیشن قوی تری میشود. به عنوان مثال می توان کلاسهای زیر را با استفاده از این اصل به کلاس دوم تغییر داد.
class Triangle {
var base = 0.0
var height = 0.0
}
class Square {
var side = 0.0
}
class AreaCalculate{
fun getTriangleArea(triangle: Triangle): Double {
return triangle.base * triangle.height / 2.also {it}
}
fun getSquareArea(square: Square): Double {
return square.side * square.side
}
}
پس از استفاده از این اصل:
interface Shape {
val area: Double
}
class Triangle : Shape {
var base = 0.0
var height = 0.0
override val area: Double
get() = base * height / 2
}
class Square : Shape {
var side = 0.0
override val area: Double
get() = side * side
}
class AreaCalculate {
fun getShapeArea(shape: Shape): Double {
return shape.area
}
}
Liskov’s Substitution Principle
این اصل نشان می دهد که کلاسهای والد باید بدون تغییر رفتار نرم افزار به راحتی با کلاسهای فرزند خود جایگزین شوند. این بدان معنی است که یک زیر کلاس باید متدهای کلاس اصلی را نادیده بگیرد که این عملکرد کلاس والد را نمی شکند. این بدان معنی است که یک زیر کلاس باید متدهای کلاس اصلی راoverrideکند که این عملکرد کلاس والد را نمی شکند. به عنوان مثال در مثال زیر هرFragmentمی خواهدInterfaceرا پیاده سازی کند. بنابراین شما باید الزامات و تغییرات در کلاس آنها را کنترل کنید. به طور خلاصه در اجرای اصلی ما هرگز نباید منطق را مدیریت کنیم.
interface ClickListener {
fun onClick()
}
class SampleFragment : ClickListener {
override fun onClick() {
decrementClickCount()
//You should manage the logic here!
}
fun decrementClickCount() {}
}
class TestFragment : ClickListener {
override fun onClick() {
incrementClickCount()
//You should manage the logic here!
}
fun incrementClickCount() {}
}
fun onButtonClick(clickListener: ClickListener) {
//Handling the changes and requirements here would be a wrong place and an incorrect solution!
clickListener.onClick()
}
Interface Segregation Principle
interface-segregation principleنشان می دهد کلاسهایی کهinterfaceها را پیاده سازی می کنند مجبور نیستند متد هایی را که استفاده نمی کنند پیاده سازی کنند. این اصل به این واقعیت مربوط می شود که بسیاری ازinterfaceهای خصوصی بهتر از یکinterfaceعمومی هستند. علاوه بر این این اولین اصل است که درinterfaceاستفاده می شود تمام اصول قبلی در کلاس ها اعمال می شود. به عنوان مثال اگرinterfaceزیر را داشته باشیمclientرا مجبور به اجرایonLongClickمی کند حتی اگر نیازی به فشار طولانی نداشته باشند. بنابراین می تواند منجر بهoverheadمتدهای استفاده نشده شود. در یک کلام برای نادیده گرفتن متدهای استفاده نشده با داشتن دوinterfaceجداگانه می تواند مفید باشد.
interface MyOnClickListener {
fun onClick(v: View?)
fun onLongClick(v: View?): Boolean
fun onTouch(v: View?, event: MotionEvent?)
}
پس از استفاده از این اصل:
interface OnClickListener {
fun onClick(v: View?)
}
interface OnLongClickListener {
fun onLongClick(v: View?): Boolean
}
interface OnTouchListener {
fun onTouch(v: View?, event: MotionEvent?)
}
Dependency Inversion Principle
این اصل نشان می دهد که ماژول های سطح بالا نباید به سطح پایین بستگی داشته باشند. بنابراین هر دو باید بهabstractionبستگی داشته باشند. علاوه بر این ،abstractionنباید به جزئیات وابستگی داشته باشد. جزئیات باید بهabstractionوابستگی داشته باشد.
در یک کلام ، کلاس ها باید به abstraction وابستگی داشته باشد اما به concretion وابستگی ندارد. به عنوان مثال در زیر یک لایهabstraction جدید از طریق اینترفیس Engine برای از بین بردن وابستگی بین دو کلاس اضافه شده است.
class Engine
class ToyCar internal constructor() {
var engine: Engine? = null
init {
Engine = Engine()
}
}
پس از استفاده از این اصل:
interface IEngine
class Engine : IEngine {
var capacity = 0.0
}
class ToyCar internal constructor(var iEngine: IEngine)
در اینجا می توان به مثال دیگری درAndroidاشاره کرد این است که اگر ما می خواهیم الگوی طراحیMVPرا پیاده سازی کنیم ، بایدPresenterرا درViewخود نگه داریم. در نتیجه ، اگر Presenter را درViewنگه داریم باعث اتصال فشرده می شود. اساساً ما باید یکinterfaceبرای جدا کردن در این شرایط ایجاد کنیم. علاوه بر این ، برخی از کتابخانه های معروف برای پیاده سازی این اصل درAndroidمانندDagger2 وجود دارد. در نتیجه استفاده از اصولSOLIDدر توسعه اندروید برای پیروی از اصولclean codeمی تواند مفید باشد. هدف این مقاله بحث در مورد اهمیت اصولSOLIDبرای توسعه دهندگان اندروید است.
سوالات خود را در قسمت کامنت ها مطرح کنید تا به آنها پاسخ بدهم.
اگر می خواهید با اصول Solid به صورت مفصل تر آشنا بشوید دوره رایگان آموزش اصول Solid در سی شارپ را مشاهده نمایید
جدیدترین ویدئوهای آموزشی
در بخش TV باگتو، آموزش های کوتاه و جدید را مشاهده نمایید
برای ارسال نظر باید وارد حساب کاربری خود شوید
ورود به حساب کاربری ثبت نام
بسیار عالی
تشکر از مطالب مفیدتون