>Java >java지도 시간 >Typescript와 Java로 \'Liskov 대체 원칙\' 적용

Typescript와 Java로 \'Liskov 대체 원칙\' 적용

PHPz
PHPz원래의
2024-08-30 06:01:32849검색

Aplicando o

개념

인터페이스

인터페이스는 클래스가 구현해야 하는 계약이나 메서드 및 속성 집합을 정의합니다. 인터페이스는 클래스가 특정 형식을 따르는지 확인하는 데 사용되지만 메서드 구현은 제공하지 않고 서명만 제공합니다.

클래스는 인터페이스를 구현할 때마다 인터페이스에 대한 모든 계약(메서드 및 특성)에 서명합니다. 각 속성과 메소드는 필수로 구현됩니다.

단단한

SOLID는 Robert C. Martin(밥 삼촌)이 제안한 객체지향 프로그래밍의 5가지 기본 원칙을 나타내는 약어입니다. 여기에서 그의 기사에 대한 자세한 내용을 읽어보실 수 있습니다.
이러한 원칙은 코드의 구조와 유지 관리를 개선하여 코드를 더욱 유연하고 확장 가능하며 이해하기 쉽게 만드는 것을 목표로 합니다. 이러한 원칙은 프로그래머가 보다 체계적인 코드를 작성하고, 책임을 나누고, 종속성을 줄이고, 리팩토링 프로세스를 단순화하고, 코드 재사용을 촉진하는 데 도움이 됩니다.

LSP 소개

약어의 "L"은 "Liskov 대체 원칙"을 의미합니다. Bob 삼촌이 이 원칙을 정의하기 위해 사용한 문구는 다음과 같습니다.

"파생 클래스는 기본 클래스를 완전히 대체할 수 있어야 합니다."

따라서 파생 클래스는 코드 수정 없이 기본 클래스를 대체할 수 있도록 파생 클래스를 기본 클래스에 최대한 가깝게 배치하는 것이 좋습니다.

이 원칙은 데이터 추상화 및 유형 이론을 기반으로 1988년 Barbara Liskov에 의해 도입되었습니다. 1986년 Bertrand Meyer가 대중화한 DBC(Design by Contracts) 개념에서 파생되었습니다.

이 원칙의 또 다른 사양은 다음과 같습니다.

하위 유형은 당황하지 않고 기본 유형으로 사용해야 합니다.

프로그래밍에서는 변화와 놀라움으로 인해 문제가 발생할 수 있습니다. 시스템 기능을 교체해야 하는 경우 새 기능은 동일한 유형의 정보를 제공해야 합니다. 그렇지 않으면 시스템이 실패할 수 있습니다. 클래스 S가 기본 클래스 T와 동일한 동작을 갖도록 하려면 클래스 S 간의 유사성의 무결성을 보장하기 위해 새로운 기능을 구현하기 위한 필수 메서드를 정의하는 계약(인터페이스 또는 추상 클래스)을 사용하는 것이 필수적입니다. 그리고 T반.

실제 적용

Sparrow와 Ostrich라는 두 하위 클래스에서 사용될 fly() 메서드가 포함된 Bird 기본 클래스를 생각해 보세요.

파일:bird.java

class Bird {
    void fly() {
        System.out.println("I can fly!");
    }
}

class Sparrow extends Bird {
    // Herda o comportamento de 'fly' da classe 'Bird'
}

class Ostrich extends Bird {
    @Override
    void fly() {
        throw new UnsupportedOperationException("I cannot fly");
    }
}

파일:bird.ts

class Bird {
  fly() {
    console.log("I can fly!");
  }
}

class Sparrow extends Bird {}

class Ostrich extends Bird {
  fly() {
    throw new Error("I cannot fly");
  }
}

발생한 문제

여기서 Sparrow 클래스는 참새가 실제로 날 수 있기 때문에 LSP를 준수합니다. 그러나 Ostrich 클래스는 동작을 근본적으로 변경하는 방식으로 voo() 메서드를 재정의하고 Ave 클래스에서 설정한 기대치를 깨뜨리기 때문에 LSP를 위반합니다.

어떻게 고치나요?

Sparrow 및 Ostrich 클래스의 각 특성을 각각의 동작을 조정하기 위해 서명해야 하는 계약(인터페이스 또는 추상 클래스, 여기서는 인터페이스를 사용함)으로 나누어 LSP를 적용해야 합니다.

파일:bird.java

interface Bird {
    String getName();
    void makeSound();
}

interface FlyingBird extends Bird {
    void fly();
}

class Sparrow implements FlyingBird {
    private String name;

    public Sparrow(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void makeSound() {
        System.out.println("Chirp chirp!");
    }

    @Override
    public void fly() {
        System.out.println(this.name + " is flying!");
    }
}

class Ostrich implements Bird {
    private String name;

    public Ostrich(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void makeSound() {
        System.out.println("Boom boom!");
    }
}

public class Main {
    public static void main(String[] args) {
        Sparrow sparrow = new Sparrow("Little Sparrow");
        sparrow.makeSound(); // Chirp chirp!
        sparrow.fly(); // Little Sparrow is flying!

        Ostrich ostrich = new Ostrich("Ostrich");
        ostrich.makeSound(); // Boom boom!
        ostrich.fly(); // Error: Method 'fly' does not exist on 'Ostrich'
    }
}

파일:bird.ts

interface Bird {
  name: string;
  makeSound(): void;
}

interface FlyingBird extends Bird {
  fly(): void;
}

class Sparrow implements FlyingBird {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  makeSound() {
    console.log("Chirp chirp!");
  }

  fly() {
    console.log(`${this.name} is flying!`);
  }
}

class Ostrich implements Bird {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  makeSound() {
    console.log("Boom boom!");
  }
}

const sparrow = new Sparrow("Little Sparrow");
sparrow.makeSound(); // Chirp chirp!
sparrow.fly(); // Little Sparrow is flying!

const ostrich = new Ostrich("Ostrich");
ostrich.makeSound(); // Boom boom!
ostrich.fly(); // Error: Method 'fly' does not exist on 'Ostrich'

분석

수정설명
새 인터페이스: makeSound()와 같이 모든 새에 공통적인 동작을 정의합니다. 모든 새는 이 인터페이스를 구현해야 합니다.

FlyingBird 인터페이스: Ave를 상속하고 날 수 있는 새에 특정한 fly() 동작을 추가합니다.

Sparrow 클래스: 참새가 날 수 있으므로 FlyingBird 인터페이스를 구현합니다. 이 클래스는 소리를 내고 날아다니는 동작을 정의합니다.

타조 클래스: 타조는 날 수 없으므로 Bird 인터페이스만 구현합니다. 이 클래스에는 fly() 메서드가 없으므로 LSP를 위반하지 않습니다.

결론

LSP는 코드가 모듈식이고 재사용 가능하며 유지 관리가 용이하도록 하는 데 중요합니다. LSP를 위반하면 새로운 하위 클래스가 도입되거나 기존 하위 클래스가 수정될 때 깨지기 쉬운 코드가 손상될 수 있습니다. 이는 슈퍼클래스에 의존하는 코드 부분에서 예기치 않은 동작이 발생할 수 있기 때문입니다.

하위 유형 대체를 사용하면 수정 없이 모듈을 확장할 수 있으며, 이는 Liskov 대체 원칙에 의해 가능해진 OCP(개방/폐쇄 원칙)가 제공하는 유연성에 필수적입니다. 계약(인터페이스 또는 추상 클래스를 통해 구현됨)은 보안 설계에 매우 중요하지만 프로그래머가 이를 잘 이해해야 레거시 소프트웨어에서 흔히 발생하는 오류를 피하는 데 도움이 됩니다. 또한 문제의 계약서를 관찰하는 것만으로도 코드를 구현하고 사용하는 방법에 대한 귀중한 지침을 제공합니다.

실질적인 의미

  1. 하위 클래스를 설계할 때 버그가 발생하거나 특별한 처리가 필요하지 않고 상위 클래스가 사용되는 모든 곳에서 사용할 수 있는지 확인하세요.
  2. 슈퍼클래스의 예상 동작을 위반하는 서브클래스를 생성하지 마세요. 유지 관리 문제와 예상치 못한 버그가 발생할 수 있습니다.

리스코프 대체 원칙을 이해하고 적용하면 개발자가 보다 예측 가능하고 안정적인 객체 지향 시스템을 만드는 데 도움이 됩니다.

위 내용은 Typescript와 Java로 \'Liskov 대체 원칙\' 적용의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.