JavaRush /جاوا بلاگ /Random-UR /ٹھوس اصول جو آپ کے کوڈ کو صاف ستھرا بنائیں گے۔
Paul Soia
سطح
Kiyv

ٹھوس اصول جو آپ کے کوڈ کو صاف ستھرا بنائیں گے۔

گروپ میں شائع ہوا۔
SOLID کیا ہے؟ SOLID مخفف کا مطلب یہ ہے: - S: واحد  ذمہ داری کا اصول۔ - O: کھلا بند اصول  ۔ - L: Liskov متبادل اصول  ۔ - I: انٹرفیس سیگریگیشن کا اصول  ۔ - D: انحصار الٹا اصول  ۔ ٹھوس اصول مشورہ دیتے ہیں کہ ماڈیولز کو کیسے ڈیزائن کیا جائے، یعنی اینٹیں جن سے ایپلی کیشن بنائی گئی ہے۔ اصولوں کا مقصد ایسے ماڈیولز کو ڈیزائن کرنا ہے جو: - تبدیلی کو فروغ دیتے ہیں - سمجھنے میں آسان ہیں - دوبارہ استعمال کے قابل ہیں تو آئیے مثالوں کے ساتھ دیکھتے ہیں کہ وہ کیا ہیں۔
class MainRepository(
    private val auth: FirebaseAuth
) {
    suspend fun loginUser(email: String, password: String) {
        try {
            auth.signInWithEmailAndPassword(email, password)
        } catch (e: Exception) {
            val file = File("errors.txt")
            file.appendText(text = e.message.toString())
        }
    }
}
ہم اس مثال میں کیا دیکھتے ہیں؟ FirebaseAuth کو کنسٹرکٹر میں پاس کیا جاتا ہے، اور ہم اسے لاگ ان کرنے کی کوشش کرنے کے لیے استعمال کرتے ہیں۔ اگر ہمیں جواب میں کوئی غلطی موصول ہوتی ہے، تو ہم ایک فائل میں پیغام لکھتے ہیں۔ اب دیکھتے ہیں کہ اس کوڈ میں کیا خرابی ہے۔ 1. S: واحد ذمہ داری کا اصول  : ایک کلاس (یا فنکشن/طریقہ) صرف ایک چیز کے لیے ذمہ دار ہونا چاہیے۔ اگر ایک طبقہ کئی مسائل کو حل کرنے کا ذمہ دار ہے، تو اس کے ذیلی نظام جو ان مسائل کے حل کو نافذ کرتے ہیں ایک دوسرے سے جڑے ہوئے ہیں۔ اس طرح کے ایک ذیلی نظام میں تبدیلی دوسرے میں تبدیلیوں کا باعث بنتی ہے۔ ہماری مثال میں، loginUser فنکشن دو چیزوں کے لیے ذمہ دار ہے - لاگ ان کرنا اور فائل میں غلطی لکھنا۔ ایک ذمہ داری ہونے کے لیے، غلطی کو ریکارڈ کرنے کی منطق کو الگ کلاس میں رکھا جانا چاہیے۔
class FileLogger{
    fun logError(error: String) {
        val file = File("errors.txt")
        file.appendText(text = error)
    }
}
class MainRepository(
    private val auth: FirebaseAuth,
    private val fileLogger: FileLogger,
) {
    suspend fun loginUser(email: String, password: String) {
        try{
            auth.signInWithEmailAndPassword(email, password)
        } catch (e: Exception) {
            fileLogger.logError(e.message.toString())
        }
    }
}
غلطی کی ریکارڈنگ منطق کو ایک الگ کلاس میں منتقل کر دیا گیا ہے اور اب لاگ ان یوزر کے طریقہ کار کی صرف ایک ذمہ داری ہے - اجازت دینا۔ اگر غلطی لاگنگ منطق کو تبدیل کرنے کی ضرورت ہے، تو ہم یہ فائل لاگر کلاس میں کریں گے، نہ کہ لاگ ان یوزر 2 فنکشن میں  ۔ لیکن ترمیم کے لیے نہیں۔ اس تناظر میں، توسیع کے لیے کھلا پن کسی کلاس، ماڈیول، یا فنکشن میں ضرورت پڑنے پر نئے رویے کو شامل کرنے کی صلاحیت ہے، اور تبدیلی کے لیے بند ہونا سافٹ ویئر اداروں کے سورس کوڈ کو تبدیل کرنے کی ممانعت ہے۔ پہلی نظر میں، یہ پیچیدہ اور متضاد لگتا ہے. لیکن اگر آپ اسے دیکھیں تو اصول کافی منطقی ہے۔ OCP اصول کی پیروی یہ ہے کہ سافٹ ویئر کو موجودہ کوڈ کو تبدیل کرنے سے نہیں بلکہ نیا کوڈ شامل کرنے سے تبدیل کیا جاتا ہے۔ یعنی، اصل میں بنایا گیا کوڈ "اچھوا" اور مستحکم رہتا ہے، اور نئی فعالیت یا تو نفاذ وراثت کے ذریعے متعارف کرائی جاتی ہے یا تجریدی انٹرفیس اور پولیمورفزم کے استعمال کے ذریعے۔ تو آئیے فائل لاگر کلاس کو دیکھتے ہیں۔ اگر ہمیں کسی دوسری فائل میں لاگ لکھنے کی ضرورت ہو تو ہم نام تبدیل کر سکتے ہیں:
class FileLogger{
    fun logError(error: String) {
        val file = File("errors2.txt")
        file.appendText(text = error)
    }
}
لیکن پھر تمام لاگز ایک نئی فائل میں لکھے جائیں گے، جس کی ہمیں ضرورت نہیں ہے۔ لیکن ہم کلاس کو تبدیل کیے بغیر اپنے لاگر کو کیسے تبدیل کرسکتے ہیں؟
open class FileLogger{

    open fun logError(error: String) {
        val file = File("error.txt")
        file.appendText(text = error)
    }

}

class CustomErrorFileLogger : FileLogger() {

    override fun logError(error: String) {
        val file = File("my_custom_error_file.txt")
        file.appendText(text = error)
    }

}
ہم FileLogger کلاس اور logError فنکشن کو اوپن کے بطور نشان زد کرتے ہیں، ایک نئی CustomErrorFileLogger کلاس بناتے ہیں اور لاگنگ کا نیا عمل لکھتے ہیں۔ نتیجے کے طور پر، ہماری کلاس فعالیت کو بڑھانے کے لیے دستیاب ہے، لیکن ترمیم کے لیے بند ہے۔ 3. L: Liskov متبادل اصول  ۔ یہ ضروری ہے کہ ذیلی طبقات اپنے سپر کلاسز کے متبادل کے طور پر کام کر سکیں۔ اس اصول کا مقصد یہ ہے کہ ڈیسنڈنٹ کلاسز کو پیرنٹ کلاسز کی جگہ استعمال کیا جا سکتا ہے جن سے وہ پروگرام کو توڑے بغیر اخذ کیے گئے ہیں۔ اگر یہ پتہ چلتا ہے کہ کوڈ کلاس کی قسم کی جانچ کر رہا ہے، تو متبادل اصول کی خلاف ورزی کی گئی ہے۔ اگر ہم جانشین کلاس اس طرح لکھیں:
class CustomErrorFileLogger : FileLogger() {

    fun customErrorLog(error: String) {
        val file = File("my_custom_error_file.txt")
        file.appendText(text = error)
    }

}
اور اب آئیے فائل لاگر کلاس کو ریپوزٹری میں CustomErrorFileLogger سے تبدیل کریں۔
class MainRepository(
    private val auth: FirebaseAuth,
    private val fileLogger: CustomErrorFileLogger,
) {

    suspend fun loginUser(email: String, password: String) {
        try{
            auth.signInWithEmailAndPassword(email, password)
        }catch(e: Exception) {
            fileLogger.logError(e.message.toString())
        }
    }

}
اس صورت میں، logError کو پیرنٹ کلاس سے کال کیا جائے گا، یا آپ کو کال کو fileLogger.logError(e.message.toString()) سے fileLogger.customErrorLog(e.message.toString()) میں تبدیل کرنا ہوگا۔ :  انٹرفیس علیحدگی کا اصول ایک مخصوص کلائنٹ کے لیے ڈیزائن کردہ انتہائی خصوصی انٹرفیس بنائیں۔ کلائنٹس کو ان انٹرفیس پر انحصار نہیں کرنا چاہئے جو وہ استعمال نہیں کرتے ہیں۔ یہ پیچیدہ لگتا ہے، لیکن یہ اصل میں بہت آسان ہے. مثال کے طور پر، آئیے FileLogger کلاس کو ایک انٹرفیس بنائیں، لیکن اس میں ایک اور فنکشن شامل کریں:
interface FileLogger{

    fun printLogs()

    fun logError(error: String) {
        val file = File("errors.txt")
        file.appendText(text = error)
    }

}
اب تمام اولادوں کو printLogs فنکشن کو لاگو کرنے کی ضرورت ہوگی، چاہے ہمیں تمام نسلی کلاسوں میں اس کی ضرورت نہ ہو۔
class CustomErrorFileLogger : FileLogger{

    override fun printLog() {

    }

    override fun logError(error: String) {
        val file = File("my_custom_error_file.txt")
        file.appendText(text = error)
    }

}
اور اب ہمارے پاس خالی فنکشنز ہوں گے، جو کوڈ کی صفائی کے لیے برا ہے۔ اس کے بجائے، ہم انٹرفیس میں ڈیفالٹ ویلیو بنا سکتے ہیں اور پھر فنکشن کو صرف ان کلاسوں میں اوور رائیڈ کر سکتے ہیں جن میں اس کی ضرورت ہے:
interface FileLogger{

    fun printLogs() {
        //Howая-то дефолтная реализация
    }

    fun logError(error: String) {
        val file = File("errors.txt")
        file.appendText(text = error)
    }

}
class CustomErrorFileLogger : FileLogger{

    override fun logError(error: String) {
        val file = File("my_custom_error_file.txt")
        file.appendText(text = error)
    }

}
اب وہ کلاسز جو FileLogger انٹرفیس کو لاگو کریں گی صاف ہو جائیں گی۔ 5. D: انحصار الٹا اصول  ۔ انحصار کا مقصد ایک تجریدی ہونا چاہئے، کوئی ٹھوس چیز نہیں۔ آئیے اپنی مرکزی کلاس پر واپس چلتے ہیں:
class MainRepository(
    private val auth: FirebaseAuth,
    private val fileLogger: FileLogger,
) {

    suspend fun loginUser(email: String, password: String) {
        try{
            auth.signInWithEmailAndPassword(email, password)
        } catch (e: Exception) {
            fileLogger.logError(e.message.toString())
        }
    }

}
ایسا لگتا ہے کہ ہم نے پہلے ہی یہاں ہر چیز کو ترتیب اور طے کر لیا ہے۔ لیکن اب بھی ایک اور نکتہ ہے جسے تبدیل کرنے کی ضرورت ہے۔ یہ FirebaseAuth کلاس استعمال کر رہا ہے۔ اگر کسی وقت ہمیں اجازت کو تبدیل کرنے اور فائر بیس کے ذریعے لاگ ان کرنے کی ضرورت نہیں، بلکہ، مثال کے طور پر، کسی قسم کی API درخواست کا استعمال کرتے ہوئے کیا ہوگا؟ تب ہمیں بہت سی چیزیں بدلنی ہوں گی، اور ہم یہ نہیں چاہیں گے۔ ایسا کرنے کے لیے، فنکشن signInWithEmailAndPassword (email: String، پاس ورڈ: String): کے ساتھ ایک انٹرفیس بنائیں۔
interface Authenticator{
    fun signInWithEmailAndPassword(email: String, password: String)
}
یہ انٹرفیس ہمارا خلاصہ ہے۔ اور اب ہم لاگ ان کے مخصوص نفاذ کرتے ہیں۔
class FirebaseAuthenticator : Authenticator{

    override fun signInWithEmailAndPassword(email: String, password: String) {
        FirebaseAuth.getInstance().signInWithEmailAndPassword(email, password)
    }

}
class CustomApiAuthenticator : Authenticator{

    override fun signInWithEmailAndPassword(email: String, password: String) {
        //другой способ логина
    }

}
اور 'مین ریپوزٹری' کلاس میں اب مخصوص نفاذ پر کوئی انحصار نہیں ہے، بلکہ صرف تجرید پر
class MainRepository (
    private val auth: Authenticator,
    private val fileLogger: FileLogger,
) {

    suspend fun loginUser(email: String, password: String) {
        try{
            auth.signInWithEmailAndPassword(email, password)
        }catch(e: Exception) {
            fileLogger.logError(e.message.toString())
        }
    }

}
اور اب، اجازت کے طریقہ کار کو تبدیل کرنے کے لیے، ہمیں ماڈیول کلاس میں صرف ایک لائن کو تبدیل کرنے کی ضرورت ہے۔
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION