ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 설계 원칙 - SOLID
    Android 2021. 3. 30. 13:40
    반응형

    1. SRP (단일 책임의 원칙: Single Responsibility Principle)

     

    소프트웨어 시스템이 가질 수 있는 최적의 구조는 시스템을 만드는 조직의 사회적 구조에 커다란 영향을 받는다.

    따라서 각 소프트웨어 모듈은 변경의 이유가 하나, 단 하나여야만 한다.

    -> 하나의 모듈은 하나의 액터에 대해서만 책임져야 한다.

    • 액터 - 변경을 요청하는 한 명 이상의 사람
    • 징후 1 : 우발적 중복

    class Employee(){
        fun calculatePay(){
            /*
                회계팀에서 기능을 정의, CFO 보고를 위해 사용
                regularHours() 메서드 사용
             */
        }
        fun reportHours(){
            /*
                인사팀에서 기능을 정의, COO 보고를 위해 사용
                regularHours() 메서드 사용
             */
        }
        fun save(){
            /*
                데이터베이스 관리자가 기능을 정의, CTO 보고를 위해 사용
             */
        }
        fun regularHours(){
            /*
                업무 시간을 계산 하기 위해 사용
             */
        }
    }
    

    Employee 단일 클래스 안에 세 액터가 서로 결합이 되어 있다.

    CFO 팀에서 결정한 조치가 COO팀이 의존하는 무언가에 영향을 줄 수 있다.

    예를 들어 CFO 팀에서 regularHours() 메서드를 수정을 하려 하고 COO팀에서 동일하게 사용하고 있는지를 모른다면 변화가 일어나면 COO팀은 regularHours()에서 잘못된 데이터를 받아오게 된다.

     

    • 징후 2 : 병합

      • 소스 파일에 다양하고 많은 메서드를 포함하면 병합이 자주 발생할 수 있다.
      • 서로 다른 액터를 책임진다면 병합이 발생할 가능성은 훨씬 높아진다.
      • 병합은 여러 사람이 서로 다른 목적으로 같은 소스 파일을 변경하는 경우 발생한다.

    예를 들어 CTO 팀에서 Employee 테이블 스키마를 수정하기로 했다.

    동시에 COO팀에서 reportHours() 메서드를 수정하기로 했다.

    두 팀이 작업을 끝내고 변경사항을 적용한다면 병합이 발생한다.

    두 팀뿐만 아니라 CFO팀에도 영향이 갈 것이다.

     

    • 해결책

      • 데이터와 메서드를 분리하는 방식이 가장 확실한 해결책이다.
      • 단점은 클래스를 인스턴스 화하고 추적해야 한다.
      • 이런 단점을 위해 퍼사드 패턴을 사용한다.

     

    class PayCalculator{
        fun calculatePay(){}
    }
    class HourReporter{
        fun reportHours(){}
    }
    class EmployeeSaver{
        fun save(){}
    
    }
    class EmployeeFacade(private val payCalculator: PayCalculator, 
    					 private val hourReporter: HourReporter, 
    					 private val employeeSaver: EmployeeSaver){
        fun regularHours(){
            payCalculator.calculatePay()
            hourReporter.reportHours()
            employeeSaver.save()
        }
    }

    퍼사드 패턴은 인터페이스를 간단하게 바꾸기 위한 디자인 패턴이다.

     

    2. OCP (개방 폐쇄의 원칙: Open Close Principle)

    기존 코드를 수정하기보다는 반드시 새로운 코드를 추가하는 방식으로 시스템의 행위를 변경할 수 있도록 설계해야만 소프트웨어 시스템을 쉽게 변경할 수 있다.

    즉, 소프트웨어 개체는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 된다는 뜻이다.

    시스템을 확장하기 쉬운 동시에 변경으로 인해 시스템이 너무 많은 영향을 받지 않도록 하는 것이 목표이다. 시스템을

    컴포넌트 단위로 분리하고, 저수준 컴포넌트에서 발생한 변경으로부터 고수준 컴포넌트를 보호할 수 있는 형태의

    의존적 계층구조가 만들어지도록 해야 한다.

     

    3. LSP (리스 코브 치환의 원칙: The Liskov Substitution Principle)

    상호 대체 가능한 구성요소를 이용해 소프트웨어 시스템을 만들 수 있으려면, 구성요소는 반드시 서로 치환 가능해야

    한다. 조금이라도 위배하면 시스템 아키텍처가 오염되어 상당량의 별도 메커니즘을 추가해야 할 수 있기 때문에

    아키텍처 수준까지 확장해야 한다.

     

    4. ISP (인터페이스 분리의 원칙: Interface Segregation Principle)

    소프트웨어 설계자는 사용하지 않은 것에 의존하지 않아야 한다.

    class User1 : OPS()
    
    class User2 : OPS()
    
    class User3 : OPS()
    
    open class OPS {
        fun op1() {}
    
        fun op2() {}
    
        fun op3() {}
    }

    위의 코드는 User들이 각자 번호에 맞는 op만 사용하지만 다른 번호의 메서드도 의존하게 된다.

     

    class User1 : U1Ops { override fun op1() {} }
    
    class User2 : U2Ops { override fun op2() {} }
    
    class User3 : U3Ops { override fun op3() {} }
    
    class OPS : U1Ops, U2Ops, U3Ops {
        override fun op1() {}
    
        override fun op2() {}
    
        override fun op3() {}
    }
    
    interface U1Ops { fun op1() }
    
    interface U2Ops { fun op2() }
    
    interface U3Ops { fun op3() }

    이런 식으로 분리를 하면 각자 사용하는 메서드만 의존하게 된다.

     

    5. DIP (의존성 역전의 원칙: Dependency Inversion Principle)

    고수준 정책을 구현하는 코드는 저수준 세부사항을 구현하는 코드에 절대로 의존해서는 안된다.

    대신 세부사항이 정책에 의존해야 한다.

    '유연성이 극대화된 시스템'이란 소스 코드 의존성이 추상에 의존하며 구체에는 의존하지 않는 시스템이다.

    반응형

    댓글

Designed by Tistory.