SOLID - 객체지향 5원칙

|

프로그래밍을 하면서, 자바 언어를 사용하면서 객체지향 5대 원칙 혹은 SOLID 원칙을 들어본적은 있지만 제대로 정리한 적이 한번도 없어서 이해하기 위해서 블로그에 정리해본다.

객체지향 5원칙에 대하여

  • 객체지향 5대 원칙은,SRP(단일책임원칙),OCP(개방-폐쇄 원칙),DIP(의존역전원칙),ISP(인터페이스 분리원칙)을 말하며 앞글자를 따서 SOLID 원칙이라고 부른다. 프로그래머가 시간이 지나도 유지보수와 확장이 쉬운 소프트웨어를 만드는데 이 원칙들을 적용할 수 있다.

1. 단일 책임 원칙 - Single Responsiblity Principle

  • 소프트웨어의 설계( 클래스, 함수 )는 단 하나의 기능만을 수행해야 한다. 즉, 응집도는 높고 결합도는 낮아야한다.

  • 지키지 않을 경우 생길수 있는 문제는, 유지보수가 어려워진다. 여러개의 기능을 구현하고 있을 경우, 그 중 하나라도 변경 될 경우 연관되어 있는 모든 클래스를 수정해야 할 수 있다. 책임을 분리해야, 새로운 요구사항과 프로그램 변경에 유연하게 대처할 수 있다.

2. 개방폐쇄원칙 - OpenClosed Principle

  • 기존의 코드를 변경하지(Closed) 않고 기능을 수정하거나 추가할 수 있도록(Open) 설계해야 한다. 즉 자주변경 되는 내용은 수정하기 쉽게 설계하고, 변경되지 않아야 하는 것은 수정되는 내용에 영향을 받지 않도록 하는 것이 포인트다.

  • 이를 위해 자주 사용되는 문법이 인터페이스이다. 아래 코드를 보면, mp3 mp4 재생을 둘다 대응할 수 있다. 만약 여기서 다른 확장자의 음악 프로그램을 실행해야 하는 경우 class 하나만 추가하면 된다. 이처럼 개방폐쇄 원칙은 기능을 추가하거나 변경해야 할경우, 이미 제대로 동작하고 있던 원래 코드르 변경하지 않아도 기존의 코드에 새로운 코드를 추가함으로써 기능의 추가나 변경이 가능하다.


interface playService {
    public void play();
}

class Mp3 implements playService {
    @Override
    public void play(){
        System.out.println("Play Mp3");
    }
}

class Mp4 implements playService {
    @Override
    public void play(){
        System.out.println("Play Mp4");
    }
}

class SoundPlayer {
    private playService service;

    public void setService(playService service){
        this.service = service;
    }

    public void play(){
        service.play();
    }
}

public class Player {
    public static void main(Strings[] args){
        SoundPlayer sp = new SoundPlayer();
        sp.setService(new MP3());
        sp.setService(new MP4());
        sp.play();
    }
}

3. 리스코프 치환 원칙 - Liskov Substitution Principle

  • 자식 클래스는 부모클래스에서 가능한 행위를 수행할 수 있어야 한다. 부모와 자식 클래스 사이의 행위에는 일관성이 있어야 하는 원칙이며, 이는 객체지향 프로그래밍에서 부모 클래스의 인스턴스 대신 자식 클래스의 인스턴스를 사용해도 문제가 없어야 한다는 것을 의미합니다.

  • 상속관계에서는 일반화 관계가 성립해야 합니다. X IS A XX다. 라는 말이 성립해야 합니다. 예를 들면 고양이과 클래스와 그걸 상속받는 호랑이 클래스가 있다.

  • 고양이 Class
    • 고양이는 균형감각이 좋다
    • 고양이들은 그루밍을 매일 한다
    • 고양이들은 박스를 좋아한다
  • 호랑이 Class
    • 호랑이는 균형감각이 좋다
    • 호랑이들은 그루밍을 매일 한다
    • 호랑이들은 박스를 좋아한다
  • 이처럼 is a 관계가 성립하면 딱히, 이상하거나 어색하지 않다. 그렇지만 만약 저기에 강아지나, 다른 포유류 동물이 들어가면 어떻게 될까?

  • 강아지는 균형감각이 좋다
  • 강아지들은 그루밍을 매일 한다
  • 강아지들은 박스를 좋아한다

  • 읽어보면, 강아지들은 보통 그루밍을 하지 않는다. 스트레스를 받거나 어딘가 아파서 하는 경우가 많다. 이처럼 is a 관계가 성립되지 못한 부모자식 관계는 LSP를 성립하지 못한 관계, 즉 일관성이 없는 관계라고 할 수 있다.

4. 의존 역전 원칙 (DIP)

  • 의존 관계를 맺을 때, 변화하기 쉬운 것 보단 변화화기 어려운 것에 의존해야 한다는 원칙이다.
  • 여기서 말하는 변화하기 쉬운것이란 구체적인 것을 말하고, 변화하기 어려운 추상적인 것을 말한다. 객체 지향적인 관점에서 보면 변화하기 쉬운 것이란 구체화된 클래스를 의미하고, 변화하기 어려운 건 추상클래스나 인터페이스를 의미한다.

  • 즉, 객체지향 프로그래밍에서는 의존관계를 맺을 경우, 인터페이스나 추상클래스와 관계 맺는 것을 선호하라는 이야기다.
  • 예를 들면, 아까만든 PlayService란 인터페이스가 아닌 구체화된 클래스를 상속받아서 사용했을 경우 avi 확장자를 추가해야 했을경우는 어떻게 될까? 이처럼 의존성 주입 기술을 사용하지 않으면 추가나 확장에서 용이하지 않을 가능성이 크다.

5. 인터페이스 분리원칙 - Interface Segregation Principle

  • 한 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다. 하나의 추상적인 인터페이스보다는, 여러개의 구체적인 인터페이스 낫다. 이는 다시 말해서 자신이 사용하지 않는 기능에는 영향을 받지 말아야 한다는 의미이다.

  • 한가지 예를 들면, 우리는 스마트폰으로 전화, 웹서핑, 사진 촬영 등 다양한 기능을 사용할 수 있다. 그런데 스마트폰에 기능이라는 인터페이스를 만들어서 그 안에 전화 웹서핑 사진 촬영의 기능을 모두 집어넣으면 어떻게 될까? 전화 기능 하나만 확장 하려고 해도 모든 메소드를 읽어보고 가독성이 떨어질 것이다. 또한 서로 영향이 가는 메소드가 있을 경우는, 시스템의 내부의존성이 높아져서 확장하거나, 변경할 경우 많은 공수가 들어갈 수 있다.

  • 참고

    • https://dev-momo.tistory.com/entry/SOLID-%EC%9B%90%EC%B9%99
    • https://vandbt.tistory.com/41
    • http://wonwoo.ml/index.php/post/1726

Comments