JavaRush /Blog Java /Random-MS /Prinsip PADAT yang akan menjadikan kod anda lebih bersih
Paul Soia
Tahap
Kiyv

Prinsip PADAT yang akan menjadikan kod anda lebih bersih

Diterbitkan dalam kumpulan
Apa itu SOLID? Inilah singkatan singkatan SOLID: - S: Prinsip Tanggungjawab Tunggal  . - O: Prinsip Terbuka-Tertutup  . - L: Prinsip Penggantian Liskov  . - I: Prinsip Pengasingan Antara Muka  . - D: Prinsip Penyongsangan Ketergantungan  . Prinsip SOLID menasihati cara mereka bentuk modul, i.e. batu bata dari mana aplikasi itu dibina. Tujuan prinsip adalah untuk mereka bentuk modul yang: - menggalakkan perubahan - mudah difahami - boleh diguna semula Jadi, mari kita lihat apa itu dengan contoh.
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())
        }
    }
}
Apa yang kita lihat dalam contoh ini? FirebaseAuth diluluskan dalam pembina, dan kami menggunakannya untuk cuba log masuk. Jika kami menerima ralat sebagai balasan, kami menulis mesej ke fail. Sekarang mari kita lihat apa yang salah dengan kod ini. 1. S: Prinsip Tanggungjawab Tunggal  : Sebuah kelas (atau fungsi/kaedah) harus bertanggungjawab untuk satu perkara sahaja. Jika kelas bertanggungjawab untuk menyelesaikan beberapa masalah, subsistemnya yang melaksanakan penyelesaian masalah ini disambungkan antara satu sama lain. Perubahan dalam satu subsistem sedemikian membawa kepada perubahan dalam subsistem yang lain. Dalam contoh kami, fungsi loginUser bertanggungjawab untuk dua perkara - log masuk dan menulis ralat pada fail. Agar terdapat satu tanggungjawab, logik untuk merekodkan ralat mesti diletakkan dalam kelas yang berasingan.
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())
        }
    }
}
Logik rakaman ralat telah dialihkan ke kelas berasingan dan kini kaedah loginUser hanya mempunyai satu tanggungjawab - kebenaran. Jika logik pengelogan ralat perlu diubah, maka kami akan melakukan ini dalam kelas FileLogger, dan bukan dalam fungsi loginUser 2. O: Prinsip Tertutup Terbuka  : Entiti perisian (kelas, modul, fungsi) mesti dibuka untuk sambungan , tetapi bukan untuk pengubahsuaian. Dalam konteks ini, keterbukaan kepada sambungan ialah keupayaan untuk menambah tingkah laku baharu pada kelas, modul atau fungsi jika timbul keperluan, dan ketertutupan untuk menukar ialah larangan menukar kod sumber entiti perisian. Pada pandangan pertama, ini kedengaran rumit dan bercanggah. Tetapi jika dilihat, prinsipnya agak logik. Mengikut prinsip OCP ialah perisian diubah bukan dengan menukar kod sedia ada, tetapi dengan menambah kod baharu. Iaitu, kod asal yang dicipta kekal "utuh" dan stabil, dan fungsi baharu diperkenalkan sama ada melalui warisan pelaksanaan atau melalui penggunaan antara muka abstrak dan polimorfisme. Jadi mari kita lihat kelas FileLogger. Jika kita perlu menulis log ke fail lain, kita boleh menukar nama:
class FileLogger{
    fun logError(error: String) {
        val file = File("errors2.txt")
        file.appendText(text = error)
    }
}
Tetapi kemudian semua log akan ditulis ke fail baru, yang, kemungkinan besar, kami tidak perlukan. Tetapi bagaimana kita boleh menukar pembalak kita tanpa mengubah kelas itu sendiri?
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)
    }

}
Kami menandakan kelas FileLogger dan fungsi logError sebagai terbuka, mencipta kelas CustomErrorFileLogger baharu dan menulis pelaksanaan pengelogan baharu. Akibatnya, kelas kami tersedia untuk melanjutkan fungsi, tetapi ditutup untuk pengubahsuaian. 3. L: Prinsip Penggantian Liskov  . Subkelas perlu berfungsi sebagai pengganti untuk kelas super mereka. Tujuan prinsip ini ialah kelas keturunan boleh digunakan sebagai ganti kelas induk yang mana ia diperoleh tanpa melanggar atur cara. Jika ternyata kod itu menyemak jenis kelas, maka prinsip penggantian dilanggar. Jika kita menulis kelas pengganti seperti ini:
class CustomErrorFileLogger : FileLogger() {

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

}
Dan sekarang mari kita gantikan kelas FileLogger dengan CustomErrorFileLogger dalam repositori
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())
        }
    }

}
Dalam kes ini, logError akan dipanggil daripada kelas induk, atau anda perlu menukar panggilan ke fileLogger.logError(e.message.toString()) kepada fileLogger.customErrorLog(e.message.toString()) 4. I : Prinsip Pengasingan Antara Muka . Cipta antara muka yang sangat khusus yang direka untuk pelanggan tertentu. Pelanggan tidak boleh bergantung pada antara muka yang mereka tidak gunakan. Kedengarannya rumit, tetapi ia sebenarnya sangat mudah. Sebagai contoh, mari jadikan kelas FileLogger sebagai antara muka, tetapi tambah satu lagi fungsi padanya:
interface FileLogger{

    fun printLogs()

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

}
Kini semua keturunan akan dikehendaki melaksanakan fungsi printLogs, walaupun kita tidak memerlukannya dalam semua kelas keturunan.
class CustomErrorFileLogger : FileLogger{

    override fun printLog() {

    }

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

}
dan sekarang kita akan mempunyai fungsi kosong, yang tidak baik untuk kebersihan kod. Sebaliknya, kita boleh membuat nilai lalai dalam antara muka dan kemudian menimpa fungsi hanya dalam kelas yang diperlukan:
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)
    }

}
Kini kelas yang akan melaksanakan antara muka FileLogger akan menjadi lebih bersih. 5. D: Prinsip Penyongsangan Ketergantungan  . Objek pergantungan haruslah abstraksi, bukan sesuatu yang konkrit. Mari kita kembali ke kelas utama kita:
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())
        }
    }

}
Nampaknya kami telah mengkonfigurasi dan membetulkan segala-galanya di sini. Tetapi masih ada satu lagi perkara yang perlu diubah. Ini menggunakan kelas FirebaseAuth. Apakah yang akan berlaku jika pada satu ketika kita perlu menukar kebenaran dan log masuk bukan melalui Firebase, tetapi, sebagai contoh, menggunakan beberapa jenis permintaan API? Kemudian kita perlu mengubah banyak perkara, dan kita tidak mahu itu. Untuk melakukan ini, buat antara muka dengan fungsi signInWithEmailAndPassword(e-mel: String, kata laluan: String):
interface Authenticator{
    fun signInWithEmailAndPassword(email: String, password: String)
}
Antara muka ini adalah abstraksi kami. Dan kini kami membuat pelaksanaan khusus log masuk
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) {
        //другой способ логина
    }

}
Dan dalam kelas `MainRepository` kini tidak ada pergantungan pada pelaksanaan khusus, tetapi hanya pada abstraksi
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())
        }
    }

}
Dan sekarang, untuk menukar kaedah kebenaran, kita perlu menukar hanya satu baris dalam kelas modul.
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION