코어 포인트
서브 클래스에서의 인복 방법은 특정 요구 사항이 충족되어야합니다. 대변인의 서명은 동일하거나 약해야합니다.
LSP 위반으로 인해 막을 수없는 행동과 추적하기 어려운 오류가 발생할 수 있습니다. 또한 서브 클래스가 슈퍼 클래스를 대체 할 수 있다는 가정이 더 이상 사실이 아니기 때문에 코드를 유지하고 확장하기가 더 어려워집니다. -
메소드 재 작성이 항상 LSP를 위반하는 것은 아닙니다. 그러나 다시 작성된 방법이 슈퍼 클래스 계약에서 예상되지 않는 방식으로 원래 방법의 동작을 변경하면 LSP를 위반합니다.
코드가 LSP를 준수하는지 확인하려면 기본 클래스 기능을 다시 작성하는 대신 (다시 쓰지 않고) 하위 클래스를 만드는 것이 가장 좋습니다. 또한, 상속 및 인터페이스 구현 대신 구성을 사용하면 LSP가 부과하는 조건의 추상화를 중단하지 않고 파생 클래스를 만드는 데 도움이 될 수 있습니다. -
허구 장면 : 해커와 매트릭스 -
다음 대화는 매트릭스 3 부작의 컷 장면에서 나옵니다.
Merpheus : Neo, 나는 지금 매트릭스에 있습니다. 이 나쁜 소식을 말해서 죄송하지만 에이전트 추적 PHP 프로그램에는 빠른 업데이트가 필요합니다. 현재 PDO의 Query () 메소드 (String)를 사용하여 데이터베이스에서 모든 행렬 에이전트의 상태를 가져 오지만 대신 전처리 쿼리를 사용해야합니다.
neo : 좋은 소리, 모르페우스. 프로그램 사본을 얻을 수 있습니까? -
<:> merphes : 문제 없어요. 저장소를 복제하고 AgentMapper.php 및 Index.php 파일을 확인하십시오.
(Neo는 일부 git 명령을 실행하고 다음 코드가 그 앞에 나타납니다)
-
nio : 모르페우스, 방금 문서를 받았습니다. pdo를 서브 클래스하고 query () 메소드를 대체하여 전처리 쿼리를 사용할 수 있도록합니다. 초강대국으로 인해이 작업을 매우 빨리 완료 할 수 있어야합니다. 침착하게 유지하십시오.
(컴퓨터 키보드의 소리가 공기 중에 반향)
nio : 모르페우스, 서브 클래스는 테스트 준비가되었습니다. 언제든지 확인하십시오.
(Murphys는 자신의 노트북에서 빠르게 검색하여 다음 수업을 보았습니다)
<:> merphes : 어댑터가 좋아 보입니다. 에이전트 매퍼가 매트릭스를 통과하는 활성 에이전트를 추적 할 수 있는지 확인하기 위해 즉시 시도해 볼 것입니다. 나에게 행운을 빕니다.
(Murphys는 잠시 주저하여 이전 Index.php 파일을 실행했습니다. 이번에는 Neo의 걸작 Pdoadapter 클래스를 사용합니다.
Merpheus : Neo, 나는 당신이 "구주"라고 믿습니다! 그러나 내 얼굴에 끔찍한 치명적인 오류가 있었고 뉴스는 다음과 같습니다.
(또 다른 비명)
neo : 무슨 일이야? ! 무엇이 잘못 되었습니까? ! (더 많은 비명)
<:> merphes : 정말 모르겠어요. 오, 스미스 요원이 지금 나를 사로 잡으러 왔습니다! (의사 소통이 갑자기 중단되었습니다. 긴 침묵이 대화를 끝내고 Morpheus가 경비원에서 잡히고 스미스 요원에 의해 심각한 부상을 입었다는 것을 시사했습니다.)
lsp는 게으르고 어리석은 프로그래머를 나타내지 않습니다
말할 것도없이, 위의 대화는 허구이지만 문제는 의심 할 여지없이 사실입니다. Neo가 Liskov 대체 원리 (LSP)에 대해 한 번만 유명했던 해커처럼 한두 가지 지식 만 배웠다면 Smith 요원은 즉시 추적 될 수 있습니다. 가장 중요한 것은 모르페우스가 요원의 악의로부터 보호됩니다. 그에게는 그런 동정심이었다. 그러나 대부분의 경우 PHP 개발자는 LSP에 대해 NEO의 이전 의견과 거의 동일하게 생각합니다. LSP는 실제로 적용이 거의없는 순수한 이론적 원칙 일뿐입니다. 그러나 그들은 잘못된 길을 갔다. LSP의 공식적인 정의는 눈부신 (나 포함)이지만, 그 핵심은 후손이 동일한 계약을 사용하는 기본 클래스 추상화와 매우 다르게 행동하는 불분명 한 클래스의 계층 구조를 피하는 것입니다. 간단히 말해서, LSP는 서브 클래스에서 방법을 다시 쓰면 다음 요구 사항을 충족시켜야한다고 규정합니다.
서명은 상위 클래스의 서명 와 일치해야합니다.
전제 조건 (수락하는 것)은 동일하거나 약해야합니다
그들의 조건 (예상되는 것)은 동일하거나 더 강해야합니다
예외 (있는 경우)는 상위 클래스 가 던진 예외 유형과 동일해야합니다.
이제 위의 목록을 다시 읽으십시오 (걱정하지 마십시오. 기다릴 것입니다). 왜 이것이 의미가 있는지 이해하고 싶습니다. 예제로 돌아가서 Neo의 치명적인 오류는 단순히 메소드 서명을 동일하게 유지하지 못하여 클라이언트 코드와 계약을 끊지 않습니다. 이 문제를 해결하기 위해, 에이전트 매퍼의 findall () 메소드는 아래와 같이 조건부 명령문 (명백한 코드 냄새)과 함께 다시 작성할 수 있습니다.<?php namespace ModelMapper;
class AgentMapper
{
protected $_adapter;
protected $_table = "agents";
public function __construct(PDO $adapter) {
$this->_adapter = $adapter;
}
public function findAll() {
try {
return $this->_adapter->query("SELECT * FROM " . $this->_table, PDO::FETCH_OBJ);
}
catch (Exception $e) {
return array();
}
}
}
기분이 좋으면 리팩토링 방법을 시도해 보면 기본 PDO 객체 또는 PDO 어댑터 인스턴스를 사용하든 잘 작동합니다. 나는 이것이 거칠게 들리지만, 빠르고 쉬운 수정 일 뿐이며, 개방 및 폐쇄의 원리를 끔찍하게 위반합니다. 반면에, 어댑터의 query () 메소드는 상위 클래스의 서명과 일치하도록 리팩토링 될 수 있습니다. 그러나 그렇게함으로써 LSP가 언급 한 다른 모든 조건도 충족되어야합니다. 요컨대, 이것은 방법 재 작성이주의해서 수행해야하며 매우 강한 이유로 만 수행 할 수 있음을 의미합니다. 많은 사용 사례에서 인터페이스를 사용할 수 없다고 가정하면 기본 클래스 기능을 재정의하지 않고 확장하는 서브 클래스를 만드는 것이 좋습니다. NEO의 PDO 어댑터의 경우,이 접근법은 완벽하게 작동하며 어느 수준에서도 클라이언트 코드를 절대로 중단하지 않습니다. 방금 말했듯이 인터페이스 구현을 활용하는보다 효율적이지만 더 급진적 인 솔루션이 있습니다. 이전 PDO 어댑터는 상속을 통해 생성되었고 LSP의 교훈을 명백히 위반했지만 실제로 결점은 에이전트 매퍼 클래스가 원래 설계된 방식에서 나옵니다. 실제로, 그것은 인터페이스 정의 계약이 아니라 위에서 아래로 구체적인 데이터베이스 어댑터 구현에 따라 다릅니다. 그리고 고대부터 큰 oo 힘이 말해 왔는데, 이것은 항상 나쁜 것입니다. 그렇다면 위의 솔루션은 어떻게 구현됩니까?
(나머지는 입력 텍스트와 비슷하며 필요에 따라 조정 및 단순화 할 수 있습니다)
위 내용은 Liskov 대체 원리의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!