코어 포인트
의존성 반전 원리 (DIP)는 높은 수준 및 저수준 모듈이 구체적인 구현보다는 추상화에 의존하도록하여 유연하고 유지하기 쉬운 코드를 생성합니다.
DIP는 단순히 "인터페이스 지향 프로그래밍"이 아니라, 종종 간과되는 미묘하지만 중요한 측면 인 높은 수준의 모듈과의 이러한 추상화를 포함하는 것도 포함됩니다.
DIP 구현은 소프트웨어 시스템의 강성 및 취약성과 같은 문제를 완화시켜 변화에 더 잘 적응하고 중단을 최소화 할 수 있습니다. -
DIP의 실제 적용은 높은 수준의 모듈 (예 : 파일 스토리지 시스템)이 프로토콜을 지정하는 코드 예제로 설명 할 수 있습니다.
DIP는 유익하지만 복잡성을 도입 할 수도 있으며, 코드 기반의 과잉 복잡성을 피하기 위해 추상화를 신중하게 관리해야하며 철저한 설계 및 아키텍처의 필요성을 강조합니다. -
각 탄탄한 원칙의 관련성에 대한 임의의 "솔로몬"결정을 내려야한다면, 나는 반전 (DIP)에 대한 의존의 원칙이 가장 과소 평가된다고 말할 것입니다. 객체 지향 설계 분야의 핵심 개념 중 일부는 처음에는 관심의 분리 및 스위칭 구현과 같은 이해하기 어렵지만, 반면에, 더 직관적이고 명확한 패러다임은 인터페이스 지향 프로그래밍과 같은 더 간단합니다. 불행하게도, 딥의 공식적인 정의는 양날의 칼과 같은 저주/축복으로 가려져 있으며, 이로 인해 프로그래머가 무시하는 경우가 많습니다. 많은 경우에 사람들이 앞서 언급 한 "인터페이스 지향 프로그래밍"명령에 대한 또 다른 진술로 기본적으로 기본적으로 기본적으로이를 무시합니다.
고급 모듈은 저수준 모듈에 의존해서는 안됩니다. 둘 다 추상화에 의존해야합니다. -
초록은 세부 사항에 의존해서는 안됩니다. 세부 사항은 추상화에 따라야합니다.
-
언뜻보기에 위의 진술은 자명 해 보입니다. 현재 구체적인 구현에 대한 강력한 의존성을 기반으로 한 시스템이 나쁜 디자인의 나쁜 징조라는 사실에 동의하지 않는 사람은 아무도 없다는 점을 감안할 때 일부 추상화를 전환하는 것이 완전히 합리적입니다. 따라서 DIP의 주요 초점은 인터페이스 지향 프로그래밍에 관한 것이라고 생각하면서 시작점으로 돌아갑니다. 실제로, 구현에서 인터페이스를 분리하는 것은이 원칙의 요구 사항을 충족시킬 때 반제 된 방법 일뿐입니다. 누락 된 부분은 실제 반전 프로세스를 구현하는 것입니다. 물론 의문이 발생합니다. 반전은 무엇입니까? 전통적으로 시스템은 항상 높은 수준의 구성 요소 (클래스 또는 프로세스 루틴이든)를 저수준 구성 요소 (세부 사항)에 의존하도록 설계되었습니다. 예를 들어, 로깅 모듈은 일련의 특정 로거 (실제로 시스템에 정보를 녹화)에 대한 종속성을 가질 수 있습니다. 따라서 로거의 프로토콜이 수정 될 때마다,이 체계는 프로토콜이 추상화 되더라도 상위 계층으로의 시끄러운 부작용을 통과합니다. 그러나 DIP의 구현은 로깅 모듈에 프로토콜이 있도록하여 이러한 잔물결을 완화하는 데 어느 정도 도움이되므로 전체 의존성 흐름을 반전시킵니다. 반전 후, 로거는 프로토콜을 충실하게 준수해야하므로 미래에 변화가 있다면 그에 따라 변경되어 프로토콜의 변동에 적응해야합니다. 요컨대, 이것은 DIP가 표준 인터페이스에만 의존하여 디커플링을 구현하는 것보다 무대 뒤에서 조금 더 복잡하다는 것을 보여줍니다. 예, 고수도 및 저수준 모듈이 추상화에 의존하는 것에 대해 논의하지만 동시에 높은 수준의 모듈에는 이러한 추상화가 있어야합니다. 미묘하지만 간과 할 수없는 미묘하지만 관련 세부 사항입니다. 예상대로 DIP가 실제로 다루는 것을 이해하는 데 도움이되는 한 가지 방법은 실용적인 코드 예제를 통해 이루어집니다. 따라서이 기사에서는 PHP 응용 프로그램을 개발할 때이 확실한 원칙을 활용하는 방법을 배울 수 있도록 몇 가지 예를 설정합니다.
간단한 스토리지 모듈 개발 (DIP에서 누락 된 "i")
많은 개발자, 특히 객체 지향적 인 PHP를 싫어하는 사람들은 딥 및 기타 견고한 원칙을 엄격한 교리로 보는 경향이 있으며, 언어에 내재 된 실용주의에 대응합니다. 나는 원칙의 실제 이점을 보여주는 야생에서 실용적인 PHP 사례를 찾기가 어렵 기 때문에이 아이디어를 이해할 수 있습니다. 나는 깨달은 프로그래머로 자신을 선전하려고하지는 않지만 (그 소송은 잘 맞지 않음), 좋은 목표를 위해 열심히 노력하고 실제 사용 사례에서 DIP를 구현하는 방법에서 실용적인 관점에서 보여주는 것이 여전히 유용합니다. 먼저 간단한 파일 스토리지 모듈의 구현을 고려하십시오. 이 모듈은 지정된 대상 파일에서 데이터를 읽고 쓰는 데 책임이 있습니다. 매우 단순화 된 수준에서 질문의 모듈은 다음과 같이 쓸 수 있습니다.<?php namespace LibraryEncoderStrategy;
class Serializer implements Serializable
{
protected $unserializeCallback;
public function __construct($unserializeCallback = false) {
$this->unserializeCallback = (boolean) $unserializeCallback;
}
public function getUnserializeCallback() {
return $this->unserializeCallback;
}
public function serialize($data) {
if (is_resource($data)) {
throw new InvalidArgumentException(
"PHP resources are not serializable.");
}
if (($data = serialize($data)) === false) {
throw new RuntimeException(
"Unable to serialize the supplied data.");
}
return $data;
}
public function unserialize($data) {
if (!is_string($data) || empty($data)) {
throw new InvalidArgumentException(
"The data to be decoded must be a non-empty string.");
}
if ($this->unserializeCallback) {
$callback = ini_get("unserialize_callback_func");
if (!function_exists($callback)) {
throw new BadFunctionCallException(
"The php.ini unserialize callback function is invalid.");
}
}
if (($data = @unserialize($data)) === false) {
throw new RuntimeException(
"Unable to unserialize the supplied data.");
}
return $data;
}
}
로그인 후 복사
로그인 후 복사
<?php namespace LibraryFile;
class FileStorage
{
const DEFAULT_STORAGE_FILE = "default.dat";
protected $serializer;
protected $file;
public function __construct(Serializable $serializer, $file = self::DEFAULT_STORAGE_FILE) {
$this->serializer = $serializer;
$this->setFile($file);
}
public function getSerializer() {
return $this->serializer;
}
public function setFile($file) {
if (!is_file($file) || !is_readable($file)) {
throw new InvalidArgumentException(
"The supplied file is not readable or writable.");
}
$this->file = $file;
return $this;
}
public function getFile() {
return $this->file;
}
public function resetFile() {
$this->file = self::DEFAULT_STORAGE_FILE;
return $this;
}
public function write($data) {
try {
return file_put_contents($this->file,
$this->serializer->serialize($data));
}
catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
public function read()
{
try {
return $this->serializer->unserialize(
@file_get_contents($this->file));
}
catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
}
로그인 후 복사
로그인 후 복사
이 모듈은 몇 가지 기본 구성 요소로 구성된 상당히 간단한 구조입니다. 첫 번째 클래스는 파일 시스템에서 데이터를 읽고 씁니다. 두 번째 클래스는 내부적으로 저장 가능한 데이터 표현을 생성하기위한 간단한 PHP 시리얼 라이저입니다. 이 샘플 구성 요소는 분리하여 비즈니스를 잘 수행하며 이와 같이 연결되어 동기식으로 작동 할 수 있습니다.
언뜻보기에 모듈의 기능이 파일 시스템에서 다양한 데이터를 쉽게 저장하고 획득 할 수 있다는 점을 감안할 때 모듈은 상당히 괜찮은 동작을 나타냅니다. 또한, Filestorage 클래스는 직렬화 가능한 인터페이스를 생성자에 주입하므로 강성 콘크리트 구현보다는 추상화로 제공되는 유연성에 의존합니다. 이러한 장점 으로이 모듈의 문제는 무엇입니까? 종종 피상적 인 첫인상은 까다 롭고 모호 할 수 있습니다. 자세히 살펴보면 Filestorage가 실제로 시리얼 라이저에 의존 할뿐만 아니라 이러한 엄격한 종속성으로 인해 대상 파일에서 데이터를 저장하고 추출하는 것은 PHP를 사용하여 기본 직렬화 메커니즘으로 제한됩니다. 데이터를 XML 또는 JSON으로 외부 서비스로 전달 해야하는 경우 어떻게됩니까? 잘 설계된 모듈은 더 이상 재사용 할 수 없습니다. 슬프지만 진짜! 이 상황은 몇 가지 흥미로운 질문을 제기합니다. 가장 먼저, Filestorage는 상호 운용 가능한 프로토콜이 이미 구현에서 분리되어 있더라도 여전히 낮은 수준의 시리얼 라이저에 대한 강력한 의존성을 보여줍니다. 둘째, 문제에서 프로토콜에 의해 공개 된 보편성 수준은 매우 제한적이며 한 시리얼 라이저를 다른 시리얼 라이저로 바꾸는 것으로 제한됩니다. 이 경우, 추상화에 의존하는 것은 환상적인 인식이며, DIP가 장려하는 진정한 반전 과정은 결코 구현되지 않습니다. 파일 모듈의 일부는 DIP 요구 사항을 충실하게 준수하도록 리팩토링 될 수 있습니다. 그렇게함으로써 Filestorage 클래스는 파일 데이터를 저장하고 추출하는 데 사용되는 프로토콜의 소유권을 얻으므로 저수준 시리얼 라이저에 대한 종속성을 제거하고 런타임에 여러 스토리지 정책을 전환 할 수 있습니다. 그렇게하면 실제로 무료로 많은 유연성을 얻을 수 있습니다. 계속 진행하여 파일 스토리지 모듈을 진정으로 딥 호환 구조로 변환하는 방법을 살펴 보겠습니다.
반전 프로토콜 소유권 및 디퍼 커플 링 인터페이스 및 구현 (DIP의 최대 사용 유지) <🎜 🎜>
옵션은 많지 않지만 프로토콜의 추상화를 유지하면서 Filestorage 클래스와 하위 수준의 공동 작업자간에 프로토콜 소유권을 효과적으로 역전시키는 방법이 여전히 남아 있습니다. 그러나 박스에서 PHP 네임 스페이스의 자연스러운 캡슐화에 의존하기 때문에 매우 직관적 인 방법이 있습니다. 이 다소 애매한 개념을 구체적인 코드로 변환하려면 모듈에 대한 첫 번째 변경 사항은 파일 데이터를 저장하고 검색하여 PHP 직렬화 이외의 형식으로 쉽게 조작 할 수 있도록 느슨한 프로토콜을 정의하는 것입니다. 아래와 같이 간소화되고 격리 된 인터페이스는 작업을 우아하고 간단하게 수행 할 수 있습니다.<?php namespace LibraryEncoderStrategy;
class Serializer implements Serializable
{
protected $unserializeCallback;
public function __construct($unserializeCallback = false) {
$this->unserializeCallback = (boolean) $unserializeCallback;
}
public function getUnserializeCallback() {
return $this->unserializeCallback;
}
public function serialize($data) {
if (is_resource($data)) {
throw new InvalidArgumentException(
"PHP resources are not serializable.");
}
if (($data = serialize($data)) === false) {
throw new RuntimeException(
"Unable to serialize the supplied data.");
}
return $data;
}
public function unserialize($data) {
if (!is_string($data) || empty($data)) {
throw new InvalidArgumentException(
"The data to be decoded must be a non-empty string.");
}
if ($this->unserializeCallback) {
$callback = ini_get("unserialize_callback_func");
if (!function_exists($callback)) {
throw new BadFunctionCallException(
"The php.ini unserialize callback function is invalid.");
}
}
if (($data = @unserialize($data)) === false) {
throw new RuntimeException(
"Unable to unserialize the supplied data.");
}
return $data;
}
}
로그인 후 복사
로그인 후 복사
인코더 린터 페이스의 존재는 파일 모듈의 전반적인 설계에 큰 영향을 미치지는 않지만 피상적으로 약속 된 것보다 더 많은 영향을 미칩니다. 첫 번째 개선은 데이터를 인코딩하고 디코딩하기위한 고도로 일반 프로토콜의 정의입니다. 두 번째 개선은 첫 번째 개선 사항만큼 중요합니다. 즉, 프로토콜의 소유권은 인터페이스가 클래스 네임 스페이스에 존재하기 때문에 이제 Filestorage 클래스에 속합니다. 요컨대, 우리는 여전히 정의되지 않은 저수준 인코더/디코더를 올바른 네임 스페이스와의 인터페이스를 작성하는 것만으로도 높은 수준의 Filestorage에 의존하게 만들었습니다. 요컨대, 이것은 DIP가 학업 베일 뒤에 옹호하는 실제 역전 과정입니다. 물론 Filestorage 클래스가 이전 인터페이스를 주입하는 도구 자로 수정되지 않으면 반전은 서투른 반쯤 시도되므로 여기에 리팩토링 된 버전이 있습니다.
filestorage는 이제 생성자에서 인코딩/디코딩 프로토콜의 소유권을 명시 적으로 선언하며, 남은 유일한 것은 파일 데이터를 여러 형식으로 처리 할 수있는 특정 저수준 인코더/디코더 세트를 작성하는 것입니다. 이러한 구성 요소 중 첫 번째는 이전에 작성된 PHP 시리얼 라이저의 리팩토링 구현입니다.
분석 시리얼 라이저의 논리는 확실히 중복됩니다. 그럼에도 불구하고, 이제는 추상화가 느슨해 지거나 디코딩되는 것뿐만 아니라 추상화의 소유권이 네임 스페이스 수준에서 명시 적으로 노출된다는 점을 지적 할 가치가 있습니다. 다시 한 번 더 나아가서 DIP의 이점을 강조하기 위해 더 많은 인코더를 작성하기 시작할 수 있습니다. 다음과 같은 또 다른 저수준 구성 요소가 있습니다. <?php namespace LibraryFile;
class FileStorage
{
const DEFAULT_STORAGE_FILE = "default.dat";
protected $serializer;
protected $file;
public function __construct(Serializable $serializer, $file = self::DEFAULT_STORAGE_FILE) {
$this->serializer = $serializer;
$this->setFile($file);
}
public function getSerializer() {
return $this->serializer;
}
public function setFile($file) {
if (!is_file($file) || !is_readable($file)) {
throw new InvalidArgumentException(
"The supplied file is not readable or writable.");
}
$this->file = $file;
return $this;
}
public function getFile() {
return $this->file;
}
public function resetFile() {
$this->file = self::DEFAULT_STORAGE_FILE;
return $this;
}
public function write($data) {
try {
return file_put_contents($this->file,
$this->serializer->serialize($data));
}
catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
public function read()
{
try {
return $this->serializer->unserialize(
@file_get_contents($this->file));
}
catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
}
로그인 후 복사
로그인 후 복사
예상대로, 추가 인코더의 기본 논리는 눈에 띄는 개선 및 변이체를 제외하고는 첫 번째 PHP 시리얼 라이저와 유사합니다. 또한, 이러한 구성 요소는 딥 제과 요구 사항을 준수하므로 Filestorage 네임 스페이스에 정의 된 인코딩/디코딩 프로토콜을 준수합니다. 파일 모듈의 상위 및 하위 수준 구성 요소는 모두 추상화에 의존하고 인코더는 파일 스토리지 클래스에 명확하게 의존하기 때문에 모듈이 딥 사양에 따라 동작한다고 안전하게 주장 할 수 있습니다. 또한 다음 예제는 이러한 구성 요소를 결합하는 방법을 보여줍니다.
모듈이 클라이언트 코드에 노출되는 간단한 미묘함과는 별도로, 핵심 요점을 설명하고 DIP의 곤경이 실제로 기존의 인터페이스 지향 프로그래밍 "패러다임보다 더 광범위한 이유를 보여주는 방법으로 매우 유용합니다. 종속성의 역전을 설명하고 명시 적으로 지정하므로 다른 메커니즘을 통해 구현해야합니다. PHP의 네임 스페이스는 너무 많은 부담없이이를 달성 할 수있는 좋은 방법이지만, 잘 구조화되고 표현적인 응용 프로그램 레이아웃을 정의하는 것과 같은 전통적인 방법은 동일한 결과를 생성 할 수 있습니다. <?php use LibraryLoaderAutoloader,
LibraryEncoderStrategySerializer,
LibraryFileFileStorage;
require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader = new Autoloader;
$autoloader->register();
$fileStorage = new FileStorage(new Serializer);
$fileStorage->write(new stdClass());
print_r($fileStorage->read());
$fileStorage->write(array("This", "is", "a", "sample", "array"));
print_r($fileStorage->read());
$fileStorage->write("This is a sample string.");
echo $fileStorage->read();
로그인 후 복사
결론 종종 주관적인 전문 지식에 근거한 의견은 종종 편향되며 물론이 기사의 시작 부분에서 표현한 견해도 예외는 아닙니다. 그러나, 의존성 추상화와 동의어로 쉽게 오해되기 때문에, 더 복잡한 고체에 대한 의존성 반전 원칙을 무시하는 경향이 약간있다. 또한 일부 프로그래머는 직관적으로 반응하고 "반전"이라는 용어를 역전을 제어하는 약어 표현으로 생각하는 경향이 있으며,이 두 사람은 서로 관련이 있지만 궁극적으로 잘못된 개념입니다. 이제 DIP의 진정한 의미를 알았으므로 모든 혜택을 활용하여 애플리케이션이 시간이 지남에 따라 발생할 수있는 취약성 및 강성 문제에 덜 취약하게 만들 것입니다. Kentoh/Shutterstock의 사진
신뢰 역전 원칙 에 대한 질문이 자주 묻습니다
의존성 반전 원리 (DIP)의 주요 목적은 무엇입니까?
의존성 반전 원리 (DIP)는 객체 지향 프로그래밍에서 견고한 원리의 핵심 측면입니다. 주요 목적은 소프트웨어 모듈을 해체하는 것입니다. 이는 복잡한 논리를 제공하는 고급 모듈이 기본 작업을 제공하는 저수준 모듈과 분리되어 있음을 의미합니다. 그렇게함으로써 저수준 모듈로 변경하면 높은 수준의 모듈에 미치는 영향을 최소화하여 전체 시스템을보다 쉽게 관리하고 유지 관리 할 수 있습니다.
DIP은 전통적인 프로그래밍 프로그래밍과 어떻게 다릅니 까?
전통적인 프로그래밍 방식 프로그래밍에는 일반적으로 저수준 모듈에 의존하는 높은 수준의 모듈이 포함됩니다. 이로 인해 한 모듈의 변경이 다른 모듈에 큰 영향을 줄 수있는 강성 시스템으로 이어질 수 있습니다. 반면에 Dip 은이 의존성을 반전시킵니다. 높은 수준 및 저수준 모듈은 모두 추상화에 의존하여 유연성을 촉진하고 시스템을 변화에보다 적응력있게 만듭니다.
DIP의 실제 적용에 대한 간단한 예를 제공 할 수 있습니까?
물론 파일에서 데이터를 읽고 처리하는 간단한 프로그램 예제를 고려해 봅시다. 전통적인 방법에서 처리 모듈은 파일 읽기 모듈에 직접 의존 할 수 있습니다. 그러나 DIP를 사용하면 두 모듈 모두 "DataReader"인터페이스와 같은 추상화에 의존합니다. 즉, 처리 모듈은 파일 읽기 모듈에 직접 제대로 묶이지 않으며 처리 모듈을 변경하지 않고도 다른 데이터 소스 (예 : 데이터베이스 또는 웹 서비스)로 쉽게 전환 할 수 있습니다.
내 코드에서 DIP를 사용하면 어떤 이점이 있습니까?
DIP는 코드에 몇 가지 이점을 가져올 수 있습니다. 디퍼 커플 링을 촉진하여 시스템을보다 유연하고 수정하기 쉽게 만듭니다. 또한 종속성을 쉽게 조롱하거나 스텁 할 수 있으므로 코드의 테스트 가능성을 향상시킵니다. 또한 구현 지향 프로그래밍보다는 인터페이스 지향 프로그래밍과 같은 훌륭한 설계 사례를 장려합니다.
DIP 구현의 결점이나 도전은 무엇입니까?
DIP에는 많은 장점이 있지만, 특히 추상화 수가 관리하기 어려워 질 수있는 대형 시스템에서도 복잡성을 도입 할 수 있습니다. 또한 인터페이스를 정의하고 다른 클래스를 만들기 위해 다른 클래스를 생성해야하므로 더 많은 코드 쓰기로 이어질 수 있습니다. 그러나 이러한 과제는 훌륭한 디자인과 건축 실습으로 완화 될 수 있습니다.
DIP는 다른 탄탄한 원칙과 어떤 관련이 있습니까?
DIP는 견고한 약어의 마지막 원칙이지만 다른 원칙과 밀접한 관련이 있습니다. 예를 들어, 단일 책임 원칙 (SRP)과 Open and Close Principle (OCP)은 DIP의 핵심 측면입니다. Richter 대체 원리 (LSP)와 인터페이스 분리 원리 (ISP)는 모두 딥의 핵심에있는 추상화를 다룹니다.
Java가 아닌 언어로 딥을 사용할 수 있습니까?
절대적으로. DIP는 일반적으로 Java 및 기타 객체 지향 언어의 맥락에서 논의되지만 원칙 자체는 언어 독립적입니다. 추상화를 지원하는 모든 언어로 인터페이스 또는 추상 클래스와 같은 딥을 적용 할 수 있습니다.
내 코드에서 DIP를 적용하기 시작하려면 어떻게해야합니까?
좋은 출발점은 높은 수준의 모듈이 저수준 모듈에 직접 의존하는 코드에서 영역을 찾는 것입니다. 이 모듈 사이의 추상화를 도입하여 해체 할 수 있는지 고려하십시오. 목표는 모든 직접 의존성을 제거하는 것이 아니라 의존성이 구체적인 구현이 아니라 추상화를 목표로하는 것입니다.
내 코드 성능을 향상시킬 수 있습니까?
DIP는 주로 성능보다는 코드의 구조와 유지 가능성을 향상시키는 데 사용됩니다. 그러나 코드를보다 모듈화하고 이해하기 쉽게함으로써 성능 병목 현상을보다 효율적으로 식별하고 해결하는 데 도움이 될 수 있습니다.
크고 복잡한 시스템에만 딥이 유용합니까?
DIP의 이점은 대규모 복잡한 시스템에서 종종 더 분명하지만 소규모 프로젝트에서도 유용 할 수 있습니다. 소규모 코드베이스에서도 디커플링 모듈은 코드를 쉽게 이해하고 테스트하고 수정할 수 있습니다.
위 내용은 의존성 반전 원리의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!