>웹 프론트엔드 >JS 튜토리얼 >JavaScript 시리즈에 대한 심층적인 이해 (8) S.O.L.I.D 다섯 가지 원칙 - Liskov 대체 원리 LSP_javascript 기술

JavaScript 시리즈에 대한 심층적인 이해 (8) S.O.L.I.D 다섯 가지 원칙 - Liskov 대체 원리 LSP_javascript 기술

WBOY
WBOY원래의
2016-05-16 17:57:021401검색

서문
이번 장에서 설명할 내용은 S.O.L.I.D 자바스크립트 언어 구현의 5가지 원칙 중 세 번째인 Liskov Substitution 원리 LSP(The Liskov Substitution 원리)입니다.

영문 원문: http://freshbrerewedcode.com/derekgreer/2011/12/31/solid-javascript-the-liskov-substitution-principle/
코드 복사
오픈 설명 닫는 원칙은 다음과 같습니다.

하위 유형은 기본 유형으로 대체 가능해야 합니다.
파생 유형은 기본 유형으로 대체 가능해야 합니다.
코드 복사
객체 지향 프로그래밍에서 상속은 하위 클래스가 기본 클래스의 코드를 공유하는 메커니즘을 제공합니다. 이는 기본 유형에 공통 데이터와 동작을 캡슐화한 다음 유형을 선언함으로써 달성됩니다. 더 자세한 하위 유형을 사용하려면 Liskov 대체 원칙을 적용하기 위해 상속된 하위 유형이 기본 유형에서 원하는 동작과 의미상 동일해야 합니다.

더 나은 이해를 위해 다음 코드를 참조하세요.

코드 복사 코드는 다음과 같습니다.

function Vehicle(my) {
var my = my || {}
my.speed = 0
my.running = false; >this.speed = function() {
return my.speed;
};
this.start = function() {
my.running = true; this.stop = function() {
my.running = false;
};
this.accelerate = function() {
my.speed; 감속 = function () {
my.speed--;
}, this.state = function() {
if (!my.running) {
return "parked"; }
else if (my.running && my.speed) {
return "moving"
}
else if (my.running) {
return "idle"; }
};
}


위 코드에서 Vehicle 함수를 정의하고 해당 생성자는 Vehicle 객체에 대한 몇 가지 기본 작업을 제공합니다. 현재 서비스에서 실행 중인 고객의 제품 환경에서 차량의 이동 속도를 높이기 위해 새로운 생성자를 추가해야 하는 경우. 고민 끝에 다음과 같은 코드를 작성했습니다.




코드 복사

코드는 다음과 같습니다.
function FastVehicle(my) { var my = my || var that = new Vehicle(my) that.accelerate = function() { my .speed = 3 ;
};
return that
}


브라우저 콘솔에서 테스트했는데 모든 기능이 예상대로 작동합니다. FastVehicle 속도 3배 증가했고 그에게서 물려받은 메서드는 예상대로 작동합니다. 그 후 우리는 이 새로운 버전의 클래스 라이브러리를 프로덕션 환경에 배포하기 시작했지만 기존 코드가 실행을 지원할 수 없게 만드는 새로운 생성자를 받았습니다.




코드 복사

코드는 다음과 같습니다.
var Maneuver = function(vehicle) { write(vehicle) .state() ); vehicle.start(); vehicle.accelerate() write(vehicle.state()); >write(vehicle .speed()); vehicle.decelerate();
write(vehicle.speed())
if (vehicle.state() != "idle") {
throw "차량이 아직 움직이고 있습니다!";
vehicle.stop()
write(vehicle.state())


위 코드에 따르면 "차량이 아직 움직이고 있습니다!"라는 예외가 발생하는 것을 볼 수 있습니다. 이는 이 코드를 작성한 작성자가 항상 가속도와 감속도가 동일하다고 믿었기 때문입니다. 하지만 FastVehicle 코드와 Vehicle 코드는 완전히 교체가 가능하지 않습니다. 따라서 FastVehicle은 Liskov 대체 원칙을 위반합니다.

이 시점에서 "그러나 고객이 항상 그러한 규칙에 따라 차량이 작동한다고 가정할 수는 없습니다"라고 생각할 수 있습니다. LSP(리스코프 대체 원칙)는 방해합니다(번역자 주: 이것이 방지하는 코드입니다). LSP 구현)은 업데이트된 코드 동작을 보장하기 위해 상속된 서브클래스가 수행해야 한다고 생각하는 것이 아니라 그러한 업데이트가 현재 기대치 내에서 구현될 수 있는지 여부에 기반합니다.

위 코드의 경우 이러한 비호환성 문제를 해결하려면 차량 클래스 라이브러리나 클라이언트 호출 코드 또는 둘 다에서 약간의 재설계가 필요합니다.

LSP 방해를 줄입니다.
그렇다면 LSP 방해를 어떻게 피할 수 있을까요? 불행하게도 이것이 항상 가능한 것은 아닙니다. 이 문제를 처리하는 방법에 대한 몇 가지 전략이 있습니다.

계약
LSP 과도한 방해를 처리하는 한 가지 전략은 계약을 사용하는 것입니다. 계약 목록은 실행 가능 사양과 오류 처리의 두 가지 형식으로 제공됩니다. 실행 가능 사양에는 세부 클래스 라이브러리도 포함되어 있습니다. 일련의 자동화된 테스트 및 오류 처리는 전제 조건, 사후 조건, 상수 검사 등 코드에서 직접 처리됩니다. 이 기술은 Bertrand Miller의 걸작 "Contract Design"에서 볼 수 있습니다. 자동화된 테스트 및 계약 설계는 이 기사의 범위를 벗어나지만 이를 사용할 때 다음을 권장합니다.

테스트 기반 개발을 사용하여 코드 설계를 안내하는 방법을 확인하세요
재사용 가능한 클래스 라이브러리를 설계할 때 계약 설계 기술을 사용하세요
직접 유지 관리하고 구현하려는 코드의 경우 계약 설계를 사용하면 불필요한 코드가 많이 추가되는 경향이 있습니다. 입력을 제어하려면 테스트를 추가하는 것이 매우 유용합니다. 계약 디자인을 사용하는 클래스 라이브러리 작성자라면 잘못된 사용법을 인지하고 사용자가 이를 테스트 도구로 사용할 수 있도록 허용해야 합니다.

상속 방지
LSP 장애를 피하기 위한 또 다른 테스트는 가능하다면 상속하지 않는 것입니다. Gamma의 걸작 "디자인 패턴 – 재사용 가능한 객체 지향 소프트웨어의 요소"에서 다음 제안을 볼 수 있습니다.

클래스 상속보다 객체 구성을 선호하세요
클래스 상속보다 객체 구성을 사용해 보세요
코드 복사
일부 책에서는 클래스에서 구성이 상속보다 나은 유일한 역할은 정적 타이핑이라고 논의합니다. 기반 언어(즉, 런타임에 변경될 수 있는 동작)에서 JavaScript와 관련된 한 가지 문제는 결합입니다. 상속을 사용할 때 상속된 하위 유형이 기본 유형과 결합됩니다. 즉, 유형에 대한 변경이 상속된 하위 유형에 영향을 미칩니다. 구성은 정적 언어와 동적 언어 모두에서 객체를 더 작게 만들고 유지 관리하기 쉽게 만드는 경향이 있습니다.

상속이 아니라 행동에 관한 것입니다
지금까지 JavaScript의 객체지향 현실을 규정하는 상속 컨텍스트를 사용하여 Liskov 대체 원칙을 논의했습니다. 그러나 Liskov 대체 원칙(LSP)의 본질은 실제로 상속에 관한 것이 아니라 행동 호환성에 관한 것입니다. JavaScript는 동적 언어입니다. 개체의 계약 동작은 개체의 유형이 아니라 개체의 예상 기능에 따라 결정됩니다. Liskov 대체 원칙은 원래 객체 디자인의 암시적 인터페이스와 동등한 상속에 대한 원칙 가이드로 생각되었습니다.

예를 들어 로버트 C. 마틴(Robert C. Martin)의 걸작 "애자일 소프트웨어 개발 원리, 패턴 및 실습"에 나오는 직사각형 유형을 살펴보겠습니다.

직사각형 예
고려해 보세요. 다음과 같은 직사각형 객체를 사용하는 프로그램
코드 복사 코드는 다음과 같습니다.

var 직사각형 = {
length: 0,
width: 0
};
[code]
정사각형은 길이( 길이)와 너비(너비)는 동일한 특수 직사각형이므로 직사각형 대신 정사각형을 만드는 것을 생각합니다. 직사각형 선언과 일치하도록 길이 및 너비 속성을 추가했지만 속성의 getter/setter를 사용하면 길이 및 너비 저장을 동기화하여 사각형이 선언되도록 할 수 있다고 생각합니다.
[code]
var square = {};
(function() {
var length = 0, width = 0;
// DefineProperty 메서드는 버전 262-5
Object.defineProperty의 새로운 기능입니다. (사각형, " 길이", {
get: function() { return length; },
set: function(value) { length = width = value; }
}); DefineProperty(square, "width", {
get: function() { return width; },
set: function(value) { length = width = value; }
}); )();


안타깝게도 직사각형이 아닌 정사각형을 사용하여 코드를 실행했을 때 문제점을 발견했습니다. 직사각형의 면적을 계산하는 방법 중 하나는 다음과 같습니다.
코드 복사 코드는 다음과 같습니다.

var g = function(사각형) {
사각형.길이 = 3; >직사각형.폭 = 4;
쓰기(직사각형.길이);
쓰기(직사각형.길이 *직사각형.폭)


이 메소드를 호출하면 결과는 예상한 12가 아닌 16입니다. 정사각형 정사각형 객체는 LSP 원칙을 위반합니다. 정사각형의 길이 및 너비 속성은 직사각형과 100% 호환되지 않음을 의미합니다. 항상 이를 명시적으로 암시하지는 않습니다. 이 문제를 해결하기 위해 프로그램을 구현하기 위해 모양 객체를 다시 디자인할 수 있습니다. 다각형의 개념을 기반으로 직사각형과 정사각형을 관련성 있게 선언합니다. 어쨌든, 우리의 목적은 리스코프 대체 원칙이 단순한 상속이 아니라 모든 방법(행위가 또 다른 행위가 될 수 있음)이라고 말하는 것입니다.
요약
리스코프 대체 원칙(LSP)은 상속 관계가 아니라 모든 메소드(메서드의 동작이 다른 메소드의 동작을 반영할 수 있는 한)를 표현합니다.
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.