Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Archives
Today
Total
관리 메뉴

The Office Lover

리스코프 치환 원칙 - Liskov Substitution Principle, LSP 본문

Design Patterns

리스코프 치환 원칙 - Liskov Substitution Principle, LSP

Michael Gary Scott 2023. 7. 27. 15:57

소개

  • 리스코프 치환 원칙은 매우 느슨한 설계 원칙이기 때문에 정상적인 상황에서 우리가 작성하는 코드는 이 설계 원칙을 위반하지 않습니다.
  • 리스코프 치환 원칙(Liskov Substitution Principle, LSP)은 상속 관계에 있는 클래스들 사이의 안정성유연성을 보장하는 핵심적인 원칙입니다. 

 

리스코프치환원칙

 

 

 

리스코프 치환 원칙이란?

  • "파생 클래스는 기초 클래스의 인스턴스를 대체하여도 프로그램의 정확성을 보장해야 한다"라는 원칙입니다.
    • 기초 클래스를 상속받은 파생 클래스는 부모 클래스의 모든 기능을 포함하고, 부모 클래스의 행위를 변경하지 않고도 자신의 특정 행위를 확장할 수 있어야 합니다.
  • 다른 설명으로는 "만약 S가 T의 하위 유형인 경우, T 유형의 객체는 프로그램을 중단하지 않고도 S 유형의 객체로 대체될 수 있다"라고 설명합니다.
    • 기본 클래스에서 참조 포인터를 사용하는 함수는 특별히 인지하지 않고도 파생 클래스의 객체를 사용할 수 있어야 하는 것을 의미합니다.
    • 하위 유형 또는 파생 클래스의 객체는 프로그램 내에서 상위 클래스가 나타나는 모든 상황에서 대체 가능하며, 프로그램이 원래 가지는 논리적인 동작이 변경되지 않으며 정확성도 유지됩니다.
  • 상속 관계에서 파생 클래스가 기초 클래스의 대체 가능한 특성을 가져야 한다는 원칙을 나타냅니다.
    • 기초 클래스를 상속받은 파생 클래스는 부모 클래스의 모든 특성을 포함하고, 부모 클래스의 행위를 변경하지 않고도 자신의 특정 행위를 확장할 수 있어야 합니다.

 

 

 

 

리스코프치환원칙

리스코프 치환 원칙의 핵심 개념

1. 상속 관계에서 대체 가능성

 상속 관계에 있는 파생 클래스는 기초 클래스의 모든 특징을 포함해야 합니다. 이로 인해 기초 클래스의 인스턴스를 파생 클래스의 인스턴스로 대체할 때에도 기존 코드가 정상적으로 동작해야 합니다.

 

2. 기초 클래스의 규약을 따름

 파생 클래스는 기초 클래스에서 정의된 메서드들의 사전 조건과 사후 조건을 지켜야 합니다. 즉, 기초 클래스의 행위를 변경하지 않고도 새로운 기능을 추가할 수 있어야 합니다.

 

[리스코프 치환 원칙(LSP)을 준수 예시]

class Rectangle {
    protected int width;
    protected int height;

    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getArea() {
        return width * height;
    }
}

class Square extends Rectangle {
    public Square(int sideLength) {
        super(sideLength, sideLength);
    }

    // 사각형과 정사각형을 같은 인터페이스로 다룰 수 있도록 오버라이드
    @Override
    public void setWidth(int width) {
        super.setWidth(width);
        super.setHeight(width);
    }

    @Override
    public void setHeight(int height) {
        super.setWidth(height);
        super.setHeight(height);
    }
}

 위 예시에서 Square 클래스는 Rectangle 클래스를 상속받아 정사각형을 구현하고 있습니다. 이렇게 상속 관계에서 기초 클래스의 규약을 준수하면서도 새로운 기능을 확장함으로써 리스코프 치환 원칙(LSP)을 준수합니다. 따라서 사각형과 정사각형을 같은 인터페이스로 다룰 수 있으며, 코드의 유지보수성확장성이 높아집니다.

 

 하지만, 위에서 보여준 예시는 사실상 객체지향의 특성인 다형성을 이용한 것으로 보일수도 있다. 만일 그렇다면 다형성과 리스코프 치환 원칙은 같은 것이라고 봐도 무방한 것일까? 리스코프 치환 원칙의 정의를 기준으로 살펴보면, 리스코프 치환 원칙과 다형성은 보기에는 비슷하지만, 실제로는 완전히 다른 의미를 말한다.

 

 

 

 

 

리스코프치환원칙

다형성(Polymorphism)

  • 같은 메서드를 사용하여 다양한 객체 타입에 대해 동작할 수 있는 능력을 나타냅니다. 
  • 주로 오버라이딩(Overriding)과 오버로딩(Overloading)을 통해 구현됩니다.
  • 다형성을 통해 코드의 재사용성가독성이 높아지며, 유연하고 확장 가능한 코드를 작성할 수 있습니다.

 

 

 

다형성(Polymorphism)과 리스코프 치환 원칙(Liskov Substitution Principle, LSP)의 차이점

 

1. 다형성(Polymorphism)

  • 메서드 오버라이딩과 오버로딩을 통해 같은 메서드 이름으로 다양한 객체 타입에 대해 동작할 수 있는 능력을 제공합니다.
  • 다형성은객체의 동적 바인딩을 통해 실행 지점에 적절한 메서드를 호출합니다.
  •  

 

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

  • 상속 관계에서 기초 클래스를 상속받은 파생 클래스가 기초 클래스를 대체할 수 있어야 한다는 원칙을 말합니다.
  • 리스코프 치환 원칙(LSP)은 상속 관계에서 안정성일관성을 유지하기 위한 원칙으로, 기초 클래스의 규약을 준수하면서도 새로운 기능을 확장함으로써 코드의 유지 보수성을 보장합니다.

 

따라서 다형성은 메서드 호출 시점에서 객체의 타입에 따라 동작을 결정하는 개념이며, 리스코프 치환 원칙은 상속 관계에서 파생 클래스가 기초 클래스의 대체 가능한 특성을 가져야 한다는 원칙입니다.

 

 

[리스코프 치환 원칙(LSP) 위반 예시]

 - 아래 예시는 새와 펭귄은 모두 비행할 수 있지만 펭귄은 수영도 할 수 있습니다. 이때, 새와 펭귄을 상속 관계로 표현하면 다음과 같은 수 있음을 보여줍니다.

class Bird {
    public void fly() {
        // 새의 비행 기능
    }
}

class Penguin extends Bird {
    @Override
    public void fly() {
        // 펭귄은 비행할 수 없으므로 비행 기능을 재정의
        throw new UnsupportedOperationException("펭귄은 비행할 수 없습니다.");
    }

    public void swim() {
        // 펭귄의 수영 기능
    }
}

 하지만, 위의 예시에서 Penguin 클래스는 Bird 클래스를 상속받았지만, fly() 메서드를 재정의하여 퓅귄은 비행할 수 없다는 예외를 발생시킵니다. 이렇게 상속 관계에서 기초 클래스(Bird)의 규약을 위반하는 경우, 리스코프 치환 원칙(LSP)을 위반하게 됩니다. 이를 준수하기 위해서는 상속 관계에서 기초 클래스의 규약을 준수하면서도 새로운 기능을 확장하는 설계가 필요합니다.

 

 

 

 

2023.07.27 - [Design Patterns] - 단일 책임 원칙 - single responsibility principle, SRP

 

단일 책임 원칙 - single responsibility principle, SRP

소개 객체지향 프로그래밍은 유지보수성과 재사용성으르 높이기 위해 다양한 설계 원칙을 제공합니다. 이 중에서도 '단일 책임 원칙'은 클래스나 모듈이 하나의 책임만을 가져야 한다는 원칙으

daniel6364.tistory.com

 

 

 

2023.06.08 - [Design Patterns] - 디자인 패턴 - 개방 폐쇄 원칙

 

디자인 패턴 - 개방 폐쇄 원칙

확장할 때는 개방, 수정할 때는 폐쇄 개방 폐쇄 원칙은 코드의 확장성 문제로 볼 수 있다. 추후 변경되는 요구 사항에 대응할 때 코드가 확장할 때는 개방, 수정할 때는 폐쇄될 수 있다면 해당 코

daniel6364.tistory.com

 

 

 


참고 서적 : 디자인패턴의 아름다움