👩🏻‍💻 Programming/OOP

SOLID(객체 지향 설계)

한국의 메타몽 2021. 7. 28. 05:35

 

좋은 소프트웨어를 설계하기 위해서는? 

 

코드의 재사용 및 유지보수가 용이한 것이 좋은 소프트웨어다.

이를 위해 결합도(Coupling)낮추고 응집도(Cohesion)높여야한다.

 

예를들어 A클래스와 B클래스가 서로 의존하고 있을 경우, A코드를 수정하면 B코드에도 영향을 끼칠 수 있다. 이런 경우를 결합도가 높다고 일컫는다. 

 

응집도는 한 모듈 내부의 처리 요소들이 서로 관련되어있는 정도를 말한다. 응집도가 높으면 해당 모듈은 하나의 책임에 집중하고 독립성이 높아져, 재사용 및 유지보수가 용이하다.

 

 

SOLID (객체지향 설계)

 

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

요약 : 한 클래스는 하나의 책임만 가진다.

 

예시로 아래 코드는 객체지향의 '다형성'을 무시하고 절차지향으로 선언되어있다.

class Unit(){
    private String name;
    private int speed;
    
    public void move(){
    	if(name.equals("슬라임")){
        	speed+=3;
        }else if(name.equals("주황버섯"){
        	if(angry) speed = 10;
            	else speed = 5;
        }else{
        	// ... 
        }
    }
}

만약 몬스터가 추가된다면 if-else구문은 끝없이 늘어날 수 있다.

또한 클래스 내부의 함수가 바뀔경우, 또다른 클래스에도 영향을 끼칠 수 있고, 나아가서 move라는 함수 자체에도 영향을 끼칠 수 있다.

위 코드를 단일 책임 원칙을 지켜 코드를 작성하면 다음과 같다.

class 슬라임 extends Unit{
	public void move(){
    		this.speed += 3;
    }
}

class 주황버섯 extends Unit{
	public void move(){
          	if(angry) speed = 10;
          	else speed = 5;
    }
}

// ...

 


 

2. OCP (Open Closed Principle) 개방 폐쇄 원칙

요약 : 자신의 확장에는 열려있으나, 주변의 변화에는 닫혀있어야 한다.

 

자주 사용된 모듈에 하나의 수정을 가할 때, 그 모듈을 이용하는 다른 모듈을 줄줄이 고쳐야 한다면 프로그램 수정이 고될것이다.

개방 폐쇄 원칙은 상위 클래스나 인터페이스를 중간에 둠으로써, 자신은 변화에 대해서 폐쇄적이지만 상위클래스나 인터페이스는 변화에 대해 확장을 개방해줄 수 있다. 

 

아래 예시를 봐보자.

*JDBC : Java DataBase Connectivity. Java로 Database와 연결하여 Data를 주고받을 수 있게하는 프로그래밍 인터페이스

 JDBC의 경우 Application에는 폐쇄적이지만, 자식클래스에게는 개방적으로 확장될 수 있도록 설계되어있다.

 


 

3. LSP (Liskov Substitution Principle) 리스코프 치환 원칙

요약 : 서브 타입은 언제나 자신의 기반(상위) 타입으로 교체 할 수 있어야 한다.

 

즉, 자식타입은 부모타입으로 교체가 가능해야한다.

 

예를들어 아래 도표를 봐보자.

직사각형의 자식클래스에 정사각형이 있다 가정하자. 정사각형은 직사각형이 될 수 있다. 고로, 자식클래스가 부모클래스로 교체될 수 있다.

하지만 반대의 케이스를 봐보자. 정사각형의 자식클래스에 직사각형이 있다 가정하자. 하지만 모든 직사각형이 정사각형이 될 수 있는것은 아니다. 고로, 자식클래스가 부모클래스로 교체될 수 없으므로, 상속관계를 유지할 수 없다.

 


 

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

요약 : 클라이언트는 자신이 사용하지 않는 메서드에 의존관계를 맺으면 안된다.

 

예를들어 다음과 같은 상속관계가 있다고 가정하자.

interface Animal {
  eat(): void;
  fly(): void;
}
class Bird implements Animal {
  eat(): void {
    console.log('새가 음식을 먹었어요!');
  }
  fly(): void {
    console.log('새가 하늘을 날았어요!');
  }
}

class Human implements Animal {
  eat(): void {
    console.log('아 배부르다');
  }
  fly(): void {
    // ????
  }
}

사람은 하늘을 날 수 없다. 고로 fly()함수는 사람 입장에서는 불필요한 메서드를 상속받는 것이 되어버린다.

이를 위해 사람이 불필요한 메서드를 상속받을 수 없게 인퍼페이스를 상속받는 인터페이스를 만들면 된다.

interface Animal {
  eat(): void;
}

interface FlyableAnimal extends Animal {
  fly(): void;
}

class Bird implements FlyableAnimal {
  eat(): void {
    console.log('새가 음식을 먹었어요!');
  }
  fly(): void {
    console.log('새가 하늘을 날았어요!');
  }
}

class Human implements Animal {
  eat(): void {
    console.log('아 배부르다');
  }
  // fly() 메서드를 구현하지 않는다. ISP 준수!
}

단, 상속에 상속을 받는 클래스가 증가한다면 SRP(단일책임원칙)에 위배될 수 있는데, 이는 프로젝트 요구 사항에 따라 SRP와 ISP중 적절히 선택하면 된다.

 


 

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

요약 : 자신보다 변하기 쉬운 것에 의존하지 말아야 한다.

 

예를 들어 아래 도표를 봐보자.

사람이 '옷'이라는 클래스에 의존을 하는데, 문제는 옷은 여러가지가 있다. 계절에 따라 봄,여름,가을 옷 등이 있기 때문이다.

이렇게 자신보다 변하기 쉬운 것에 의존을 하면, 코드 유지보수에 어려움이 생길 수 있다.

 

가운데 '옷'이라는 인터페이스를 둠으로써 사람은 상대적으로 변하기 어려운 것에 의존하게 됐다.

이를 코드로 구현하면 다음과 같다.

 

public class Person {

    private Clothe clothe;

    public void wearClothe(Clothe clothe) {
        this.clothe = clothe;
    }

    public void picnic() {
        clothe.picnic();
    }
}

wearClothe를 통해 Clothe 인터페이스를 구현한 클래스를 외부에서 주입해 옷을 입는다.

public interface Clothe {
    public void picnic();
}
public class Summer implements Clothe {

    @Override
    public void picnic() {
        System.out.println("여름옷을 입고 피크닉을 간다.");
    }
}

public class Winter implements Clothe {

    @Override
    public void picnic() {
        System.out.println("겨울옷을 입고 피크닉을 간다");
    }
}
public class Main {

    public static void main(String[] args) {
        Person person = new Person();
        person.wearClothe(new Summer());
        person.picnic();

        person.wearClothe(new Winter());
        person.picnic();
    }
}

 


 

참고자료

https://ko.wikipedia.org/wiki/SOLID_(객체_지향_설계) 

 

SOLID (객체 지향 설계) - 위키백과, 우리 모두의 백과사전

 

ko.wikipedia.org

 

'👩🏻‍💻 Programming > OOP' 카테고리의 다른 글

객체와 객체지향, 그리고 객체지향 4대 특성  (0) 2021.07.27
[Spring] MVC 패턴  (0) 2021.06.08