JavaRush /Java Blog /Random-TW /堅實的原則將使您的程式碼更加清晰
Paul Soia
等級 26
Kiyv

堅實的原則將使您的程式碼更加清晰

在 Random-TW 群組發布
什麼是固體? 以下是 SOLID 縮寫的意思: - S:單一 責任原則。-O:開閉原則 。-L:里氏替換原理 。- I:介面隔離原則 。-D:依賴倒置原則 。SOLID 原則建議如何設計模組,即 建立應用程式的磚塊。這些原則的目的是設計模組: - 促進變革 - 易於理解 - 可重複使用 因此,讓我們透過範例來了解它們是什麼。
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())
        }
    }
}
錯誤記錄邏輯已移至單獨的類,現在 loginUser 方法只有一個職責 - 授權。如果需要更改錯誤日誌邏輯,那麼我們將在 FileLogger 類別中執行此操作,而不是在 loginUser 2 函數中。O:開放-封閉 原則:軟體實體(類別、模組、函數)必須對擴展開放,但不用於修改。在這種情況下,擴展開放性是指在需要時向類別、模組或函數添加新行為的能力,而變更封閉性是指禁止更改軟體實體的原始碼。乍一看,這聽起來很複雜而且矛盾。但如果你仔細看一下,你會發現這個原理是非常合乎邏輯的。遵循 OCP 原則是,軟體的變更不是透過更改現有程式碼,而是透過新增程式碼。也就是說,最初創建的程式碼保持「完整」和穩定,並且透過實現繼承或透過使用抽象介面和多態性來引入新功能。那麼讓我們來看看 FileLogger 類別。如果我們需要將日誌寫入另一個文件,我們可以更改名稱:
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:里氏代換原理 。子類別必須能夠取代其超類別。這原則的目的是可以使用後代類別來代替派生它們的父類,而不會破壞程式。如果事實證明程式碼正在檢查類別的類型,則違反了替換原則。如果我們這樣寫後繼類別:
class CustomErrorFileLogger : FileLogger() {

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

}
現在讓我們用儲存庫中的 CustomErrorFileLogger 來取代 FileLogger 類
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()) 4. I : 介面隔離原則 創建專為特定客戶設計的高度專業化的介面。客戶端不應依賴他們不使用的介面。聽起來很複雜,但實際上非常簡單。例如,讓我們將 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 類別。如果在某些時候我們需要更改授權並不會透過 Firebase 登錄,而是使用某種 API 請求(例如),會發生什麼情況?然後我們將不得不改變很多事情,但我們不希望這樣。為此,請使用函數signInWithEmailAndPassword(email:String,password: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) {
        //другой способ логина
    }

}
在「MainRepository」類別中,現在不依賴具體實現,而僅依賴抽象
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