ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Swift 코드로 SOLID 이해하기
    개발/프로그래밍 2022. 5. 1. 18:53

    SRP (단일 책임 원칙)

    객체는 하나의 책임을 가진다. 여러 책임을 가지면 안된다. 따라서 객체의 역할을 정의하고 책임 주도설계 해야한다.

     

    OCP (개발 폐쇄 원칙)

     

    객체의 확장에는 열려있고 수정에는 닫혀있어야한다.

    기존 코드를 변경하지 않고 기능확장 할수 있도록 하느것이 목적이다.

    class Developer : Employee{
    	func develop()
    }
    
    class Designer : Employee{
    	func design()
    }
    
    class Marketer : Employee{
    	func marketing()
    }
    
    class Company{
    	let workers : [Employee]
    	func doWork(){
    		for worker in workers{
    			if(worker is Developer){
    				worker.develop()
    			}else if(worker is Designer){
    				worker.design()
    			}else{
    				worker.marketing()
    			}
    		}
    		
    	}
    }
    
    

    위와같은 코드는 새로운 Employee 클래스가 만들어 질때마다 doWork()함수도 변경이 반응되어야 한다.

    따라서 아래와같이 변경이 가능하다.

    class Employee{
    	func work()
    }
    
    class Developer : Employee{
    	func work()
    }
    
    class Designer : Employee{
    	func work()
    }
    
    class Marketer : Employee{
    	func work()
    }
    
    class Company{
    	let workers : [Employee]
    	func doWork(){
    		for worker in workers{
    			workers.work()
    		}
    	}
    }
    
    

     

    3.LSP (리스코브 치환원칙)

     

     

    부모객체의 역할은 자식객체도 할수있어야한다.

    ( 자식 = 부모 + a )

    따라서 상속을 받을지 안할지 잘생각해야한다. 무조건 받는게 좋은건 아니다.

    상속은 결합도를 높이기 때문이다.

     

     

    ISP (인터페이스 분리원칙)

     

    인터페이스도 하나의 책임만을 가져야한다.

    protocol Smartphone{
    	func takePhoto()
    	func call()
    }
    
    clsss BrickPhone : Smartphone{
    	func takePhoto() //벽돌폰에는 카메라가없다.
    	func call()
    }
    

    위와같은 경우 인터페이스를 상속받지만 필요없는 메소드도 상속을 받아버렸다.

    protocol Camera{
    	func takePhoto()
    }
    
    protocol Telephone{
    	func call()
    }
    

    위와같이 변경 가능하다.

     

     

    SOLID Swift 코드로 이해하기

    고수준 - 추상화되어있는 개념(인터페이스)

    저수준 - 구현체 클래스

    고수준은 추상화된 개념이라 코드 수정이 적다. 반면 구현체인 저수준은 지속적인 코드 변경이 생기게된다.

    그래서 코드를 짤때 고수준의 클래스(인터페이스) 에 의존 할수록 좋다.

    class Repository{
    	let memory : LocalDB
    	init(memory:LocalDB){
    		self.memory = LocalDB()
    }
    
    class Repository2{
    	let memory : RemoteDB
    	init(memory:RemoteDB){
    		self.memory = RemoteDB()
    }
    

    위와 같은 경우는 구현체인 Repository 에서

    직접 LocalDB()를 생성해주었다.

    만약 이런경우 다른 DB를 사용하고싶은경우 변경이 불가하다.

    다른 Repository를 만들어야 할것이다.

    같은 class 를 써도 상황에 따라 다른 DB를 쓰고싶다면

    더 고수준인 클래스를 사용하면 된다.

    class LocalDB : DB {}
    
    class RemoteDB : DB {}
    
    class Repository{
    	let memory : DB
    	init(memory:DB){
    		self.memory = memory
    }
    
    let localRepo = Repository(memory:LocalDB())
    let remoteRepo = Repository(memory:remoteDB())
    

    위와같이 고수준 클래스 를 사용하면 원하는대로 DB를 넣어줘서 사용할수있다.

    아래 코드는 아까 위코드와 다른 점이 있다. 바로 DB() 의 생성 위치이다. 위 코드는 class 내부에서 DB를 생성했지만 아래는 class 외부에서 생성후에 파라미터로 주입해주었다.

    이를 의존성 주입이라 한다. 의존성 주입을 통해 Repository가 좀더 자유로워 졌으며 테스트 코드 작성에도 유리해졌다.

    댓글

Designed by Tistory.