Clean Architecture

[Clean Architecture] SOLID원칙 02. SRP(Single Responsibility Principle) 단일 책임 원칙

clamp 2023. 5. 5. 16:17

SRP(Single Responsibility Principle) 개념

 

하나의 객체는 하나의 책임을 가져야 한다. 즉 하나의 class가 여러 기능을 담당하면 안된다.

 

예제코드

class LoginService {
    func login(id: String, pw: String) {
        let userData = requestLogin()
        let user = decodeUserInform(data: userData)
        saveUserOnDatabase(user: user)
    }
    
    private func requestLogin() -> Data {
        // Call API
        return Data()
    }
    
    private func decodeUserInform(data: Data) -> User {
        // Decoding User Inform from Data
        return User(name: "", age: 10)
    }
    
    private func saveUserOnDatabase(user: User) {
        // Save User
    }
}

예를 들어 LoginService라는 클래스에서 DB, Decoder, APIHandler의 많은 역할을 중구난방으로 가지고있다.

 

좋은 예

protocol APIHandlerProtocol{
    func requestLogin() -> Data
}

protocol DecodingHandlerProtocol{
    func decode<T>(from data: Data) -> T
}

protocol DBHandlerProtorocl{
    func saveOnDatabase<T>(inform: T)
}

class LoginService {
    
    let apiHandler: APIHandlerProtocol
    let decodingHandler: DecodingHandlerProtocol
    let dbHandler: DBHandlerProtorocl
    
    init(apiHandler: APIHandlerProtocol,
         decodeingHandler: DecodingHandlerProtocol,
         dbHandler: DBHandlerProtorocl){
        
        self.apiHandler = apiHandler
        self.decodingHandler = decodeingHandler
        self.dbHandler = dbHandler
    }
    func login(id: String, pw: String) {
        let loginData = apiHandler.requestLogin()
        let user: User = decodingHandler.decode(from: loginData)
        dbHandler.saveOnDatabase(inform: user)
    }
}

나쁜 예와 비교하면 각각의 DB, Decoder, APIHandler 역할을 하는 프로토콜을 만들어주고 각자의 역할만 하는 메서드만을 구현하게 했다. 그리고 LoginService에서는 단지 이 프로토콜들을 활용해서 상호작용만을 하고 있다.

 

이전과 비교해서 확실히 LoginService는 각각의 모듈들을 활용해서 로그인에 관한 책임만을 가지고 있다.

 

이러한 LoginService클래스를 활용하여 로그인을 수행하는 main함수를 만들 수 있다.

// 사용할 구현체를 만듭니다.
class MyAPIHandler: APIHandlerProtocol {
    func requestLogin() -> Data {
        // API 요청을 보내고 응답 데이터를 받아온다
        return Data()
    }
}

class MyDecodingHandler: DecodingHandlerProtocol {
    func decode<T>(from data: Data) -> T {
        // 데이터를 디코딩하여 사용 가능한 객체로 변환한다.
        return data as! T
    }
}

class MyDBHandler: DBHandlerProtorocl {
    func saveOnDatabase<T>(inform: T) {
        // 변환된 객체를 DB에 저장한다.
    }
}

func main() {
    // 의존성을 주입한다.
    let apiHandler = MyAPIHandler()
    let decodingHandler = MyDecodingHandler()
    let dbHandler = MyDBHandler()

    // 의존성을 가지는 인스턴스를 생성한다.
    let loginService = LoginService(apiHandler: apiHandler,
                                    decodeingHandler: decodingHandler,
                                    dbHandler: dbHandler)

    // 로그인을 시도한다.
    loginService.login(id: "my_id", pw: "my_password")
}