bumdream / morning_study

1 stars 0 forks source link

3일차 SOLID - LSP #3

Open bumdream opened 6 years ago

bumdream commented 6 years ago

LSP (리스코브 치환의 원칙 - The Liskov Substitution principle)

다음 코드를 봐보자.

SamiObject samiObject = new SamiObject();

위와 같은 코드가 있을 때, 아래와 같이 쓰라는 것이다.

SubtitleObject subtitleObject = new SamiObject();

이렇게 하면 나중에 SamiObject말고 다른 하위 객체를 쓰고 싶을 때, 저 부분만 바꿔주면 되기 때문에 수정이 편해지는 장점이 있다.

hongsukchoi commented 6 years ago

정의

치환성(영어: substitutability)은 객체 지향 프로그래밍 원칙이다. 컴퓨터 프로그램에서 자료형 S가 자료형 T의 하위형이라면 필요한 프로그램의 속성(정확성, 수행하는 업무 등)의 변경 없이 자료형 {\displaystyle T} T의 객체를 자료형 S의 객체로 교체(치환)할 수 있어야 한다는 원칙이다. [출처: 위키]

OCP vs LSP with Example

OCP but not LSP

IPerson은 OCP를 지키고 있다.

public interface IPerson {}

public class Boss implements IPerson {
    public void doBossStuff() { ... }
}

public class Peon implements IPerson {
    public void doPeonStuff() { ... }
}

public class Context {
    public Collection<IPerson> getPersons() { ... }
}

하지만 아래의 코드 사용에서 보듯이 Type Checking과 Type Conversion이 필요하다 = LSP를 위반한다

// in some routine that needs to do stuff with 
// a collection of IPerson:
Collection<IPerson> persons = context.getPersons();
for (IPerson person : persons) {
    // now we have to check the type... :-P
    if (person instanceof Boss) {
        ((Boss) person).doBossStuff();
    }
    else if (person instanceof Peon) {
        ((Peon) person).doPeonStuff();
    }
}

LSP를 지키도록 바꾸면 올바른 예는 다음과 같다.

public class Boss implements IPerson {
    // we're adding this general method
    public void doStuff() {
        // that does the call instead
        this.doBossStuff();
    }
    public void doBossStuff() { ... }
}

public interface IPerson {
    // pulled up method from Boss
    public void doStuff();
}

// do the same for Peon
// in some routine that needs to do stuff with 
// a collection of IPerson:
Collection<IPerson> persons = context.getPersons();
for (IPerson person : persons) {
    // yay, no type checking!
    person.doStuff();
}

Follows LSP but not OCP

Type Checking이 필요없으므로 LSP를 지킨다고 할 수 있다.

public class LiskovBase {
    public void doStuff() {
        System.out.println("My name is Liskov");
    }
}

public class LiskovSub extends LiskovBase {
    public void doStuff() {
        System.out.println("I'm a sub Liskov!");
    }
}

public class Context {
    private LiskovBase base;

    // the good stuff
    public void doLiskovyStuff() {
        base.doStuff();
    }

    public void setBase(LiskovBase base) { this.base = base }
}

하지만 LiskovBase의 doStuff 메소드가 Extension에는 Open되있지만, 완전히 코드가 변경되므로 Closed for Modification이 아니어서 OCP를 지키지 못한다

https://softwareengineering.stackexchange.com/questions/178488/lsp-vs-ocp-liskov-substitution-vs-open-close

bumdream commented 6 years ago

아주 좋은 내용이네요!!! 이해 되었습니다~!

tituvely commented 6 years ago

하지만 LiskovBase의 doStuff 메소드가 Extension에는 Open되있지만, 완전히 코드가 변경되므로 Closed for Modification이 아니어서 OCP를 지키지 못한다

이 부분이 잘 이해가 안가네욥ㅠㅠ