Dependency Injection
Nếu chúng ta vẫn biết về nguyên tắc SOLID thì biết cho tới phép tắc ở đầu cuối được nhắc tới là Đảo ngược dựa vào – Dependency Inversion Principle (DIP). Nhắc lại phép tắc này, tất cả chúng ta với quyết định nghĩa:
+ Các module cung cấp cao tránh việc tùy theo module thấp cấp.
Bạn đang xem: dependency là gì
+ Các class tiếp xúc cùng nhau nên trải qua interface, tránh việc trải qua những implemention.
Chi tiết hoàn toàn có thể xem xét lại vô nội dung bài viết ” Nguyên lý solid PHP “.
Dựa vô cơ có một định nghĩa tất cả chúng ta tiếp tục tìm hiểu hiểu vô nội dung bài viết này là Dependency Injection. Dependency Injection là một trong design pattern được cho phép xóa khỏi sự dựa vào hard code, thực hiện mang lại phần mềm đơn giản dễ dàng không ngừng mở rộng na ná maintain về sau.
Có 3 định nghĩa cần thiết phân biệt:
Dependency Inversion: Đây là nguyên tắc nhằm design và ghi chép code.
Inversion of Control: Đây là một trong design pattern được đưa đến nhằm code vâng lệnh nguyên tắc Dependency Inversion. Có nhiều phương pháp để tiến hành pattern này: ServiceLocator, Event, Delegate… và Dependency Injection chỉ là một trong trong những cơ hội cơ.
Inversion of Control (IoC)
Dependency Injection gom tất cả chúng ta không ngừng mở rộng code, và hạn chế sự dựa vào trong những Dependency cùng nhau. Tuy nhiên thời điểm này code của tất cả chúng ta nên kiêm thêm thắt trọng trách đựa những tùy theo ( trải qua, constructor, setter, ….).
Ví dụ:
Chúng tao tiến hành hành vi checkout giỏ mặt hàng Lúc cơ cần thiết tiến hành. Chú ý phía trên đơn giản ví dụ, vô thực tiễn tất cả tiếp tục không giống cút thật nhiều.
+ Lưu tài liệu vô database.
+ Ghi log nội dung.
+ Thông báo cho những người người sử dụng qua chuyện gmail.
Đoạn mã ví dụ tiếp tục như sau:
<?php $db = new MysqlDatabase; $notify = new EmailNotify; $logger = new FileLogger; $cart = new Cart($db,$logger,$notify); $cart->checkout();
Câu chất vấn đề ra kể từ ví dụ bên trên là, nếu mà tất cả chúng ta với thật nhiều dựa vào thì tiếp tục như vậy nào? Không nên là khi này sẽ nên hàm khởi tạo ra tiếp tục rất rất nhiều năm và rối sao. Chưa nói tới là tất cả chúng ta cũng phải tạo mặt hàng tá đối tượng người dùng đi vào.
Để cắt giảm chừng phiền hà của đoạn mã hoàn toàn có thể set dựa vào qua chuyện những setter, tuy rằng vậy sức lực chi ra mang lại công tác vì vậy rất cao.
Và tất cả chúng ta ước rằng giá chỉ với khí cụ hoặc ai cơ thực hiện hộ phần này thì chất lượng biết bao nhiêu. Và Inversion of Control đó là biện pháp nhằm tất cả chúng ta tiến hành việc này.
Theo wiki: Inversion of Control là 1 phép tắc lập trình sẵn, luồng tinh chỉnh và điều khiển của phần mềm thì ko được vận hành bởi vì phần mềm này mà bởi vì 1 sườn khái niệm cơ bạn dạng không giống.
Phân tích về Dependency Injection.
Bài toán ví dụ:
Chúng tao thi công phần mềm dùng hạ tầng tài liệu với Mysql. Đương nhiên Lúc cơ, những lớp của tất cả chúng ta tiếp tục dạng như thể Mysqlconnection… nhằm tiến hành thao tác với Mysql. Nhưng một ngày đẹp mắt trời vì như thế đòi hỏi người tiêu dùng,hoặc giản dị và đơn giản là tất cả chúng ta quí thay đổi sang trọng SQL Server thì tiếp tục nên thực hiện như vậy nào? Chúng tao nên phiên từng tệp tin nhằm sửa, điều này vô nằm trong tốn sức lực, thời hạn mà còn phải đựng được nhiều rủi ro…
Khi cơ Dependency Injection đó là biện pháp tất cả chúng ta xử lý bài xích toàn này.
Các cách thức tiến hành Dependency Injection
Tham khảo ở https://en.wikipedia.org/wiki/Dependency_injection#Interface_injection.
+ Constructor Injection: Các dependency sẽ tiến hành truyền vô 1 class trải qua hàm khoải tạo ra, Đây là cơ hội phổ biến nhất được dùng nhiều ngữ điệu lập trình sẵn và nhiều framework. Với PHP thì hoàn toàn có thể thấy ở Laravel hoặc Magento…
+ Setter Injection: Các dependency sẽ tiến hành truyền vô 1 class trải qua những hàm Setter/Getter.
+ Interface Injection.
Ưu và điểm yếu kém của Dependency Injection
Ưu điểm
+ Giảm hard code, tạo ra vui nhộn trong những module và cả linh động trong những công việc không ngừng mở rộng hành động của một class.
+ Code dễ dàng dạng duy trì, thay cho thế, tăng cấp.
+ Dễ test, unit test.
+ Cho luật lệ cải tiến và phát triển mặt khác nhiều module tác dụng vì thế sự dựa vào kết nối trong những module chỉ với tùy theo những interface nhưng mà thôi.
+ Đây là 1 cách thức thường được sử dụng trong những công việc refactoring (tái cấu trúc) code.
Nhược điểm
+ Khó hiểu với những người mới nhất, na ná khó khăn xác lập được đối tượng người dùng này đang rất được Injection vô class lúc này.
+ Khó debug, và tất cả chúng ta nên coi nhiều tệp tin, class rộng lớn nhằm thực nắm rõ công tác.
+ Cũng có một số giới hạn Lúc dùng nhằm tìm hiểu code nhanh chóng bên trên IDE, tìm hiểu tư liệu, hoặc xử lý những tham lam chiếu…
+ Các đối tượng người dùng được tạo ra kể từ thuở đầu thực hiện hạn chế performance
Áp dụng Dependency Injection với PHP
Không chỉ vô Asp, java (spring)… nhưng mà ngay lập tức vô PHP tất cả chúng ta cũng hoàn toàn có thể vận dụng những nghệ thuật này. Mà điển hình nổi bật nhất vô PHP đó là cơ hội vận hành của Laravel.
Xem thêm: Review sảnh game CQ9 tại BK8 chi tiết cho tân thủ
Chúng tao nằm trong thi công 1 ví dụ mang lại việc này.
Vẫn là ví dụ bên trên với việc checkout giỏ mặt hàng.
Khai báo những interface mang lại hành vi cần thiết xử lý.
<?php // Thực hiện tại việc ghi log dữ liệu interface Logger{ public function write($text):bool; } // Xử lý liên kết database interface Database{ public function save($table ,$data = [],$where= []):bool; } // Xử lý trọng trách thông báo interface Notify{ public function send($message) :bool; }
Tiếp cho tới là những lớp dẫn xuất của những interface trên:
<?php class MysqlDatabase implements Database{ public function save($table ,$data = [],$where= []):bool{ echo sprintf('%s save data to tát table %s'.PHP_EOL,static::class,$table); return true; } } class SqlServerDatabase implements Database{ public function save($table ,$data = [],$where= []):bool{ echo sprintf('%s save data to tát table %s'.PHP_EOL,static::class,$table); return true; } } class FileLogger implements Logger{ public function write($text):bool{ echo sprintf('%s write log %s'.PHP_EOL,static::class,$text); return true; } } class EmailNotify implements Notify{ public function send($message) :bool{ echo sprintf('%s send notify %s'.PHP_EOL,static::class,$message); return true; } }
Cuối nằm trong là class xử lý những phụ thuộc:
class DI{ private $container = []; public function register($type,$instance){ echo sprintf('%s register %s =>> %s'.PHP_EOL,static::class,$type,get_class($instance)); $this->container[$type] = $instance; } public function hasType($type):bool{ return array_key_exists($type,$this->container); } public function resolve($type){ if($this->hasType($type)){ return $this->container[$type]; } else{ if(interface_exists($type)){ throw new \Exception('Can not create instance of interface '.$type.'!'); } else if(class_exists($type)){ $ref = $this->getReflectionClassByType($type); $ins = $this->createInstanceType($ref); return $ins; } else{ throw new \Exception('Class '.$type.' is not found!'); } throw new \Exception('Instance of '.$type.' is not found!'); } } protected function getReflectionClassByType($type){ $ref = new ReflectionClass($type); if (! $ref->isInstantiable()) { throw new \Exception('Can not create instance of'.$type.'!'); } return $ref; } protected function createInstanceType($ref){ $constructor = $ref->getConstructor(); if(!isset($constructor)){ $ins = $ref->newInstance(); } else{ $params = $constructor->getParameters(); $parameters = []; foreach($params as $param){ $paramClass = $param->getClass()->getName(); $parameters[] = $this->resolve($paramClass); } $ins = $ref->newInstanceArgs($parameters); } return $ins; } }
Trong class này tất cả chúng ta tiếp tục cút cụ thể từng phần vô class.
Class DI tiếp tục có:
+ 1 mảng container nhằm chứa chấp những dựa vào và được khai báo.
+ hàm register nhằm ĐK một dựa vào mới nhất.
+ Hàm resolve: tiến hành với thông số nguồn vào là tên gọi dựa vào và Search Engine Results Page là đối tượng người dùng thích hợp.
Trong hàm resolve này còn có chú ý:
+ Kiểm tra coi loại dựa vào này đã tồn bên trên vô container ko, nếu như với rồi trả về thành phẩm, nếu như chưa tồn tại mới nhất thực ganh đua việc tạo ra đối tượng người dùng.
+ Nếu loại thông số truyền vô là dạng interface, thì loại đối tượng người dùng này sẽ không thể tự động hóa khởi tạo ra nhưng mà phải tạo trước hoàn thành tiếp sau đó register vô khối hệ thống. Trong tình huống đối tượng người dùng ứng ko tồn bên trên tiếp tục ném rời khỏi nước ngoài lệ.
+ Nếu loại đối tượng người dùng truyền vô là dạng class và cũng ko tồn bên trên vô khối hệ thống tiếp tục kế tiếp resolve liên tiếp nhằm khởi tạo ra đối tượng người dùng ứng.
Kết ngược trả về của hàm resolve này chỉ mất 2 kết quả:
+ Một là sẽ sở hữu được đối tượng người dùng trả về chính theo phong cách tài liệu đi vào.
+ Ném rời khỏi nước ngoài lệ vì thế thông số truyền vô ko hợp thức, hoặc class ko tồn bên trên, hoặc ko thể khởi tạo ra đối tượng người dùng.
Để tiện lợi mang lại việc gọi và dùng tao hoàn toàn có thể ghi chép thêm thắt class App.php như sau:
class App{ private static $di; private static function getDI(){ if(!isset(static::$di)){ static::$di = new DI; } return static::$di; } public static function make($type){ return static::getDI()->resolve($type); } public static function register($type,$instance){ return static::getDI()->register($type,$instance); } }
Khi cơ class Cart của tất cả chúng ta như tiếp sau đây.
Chú ý đoạn này tôi bổ sung cập nhật thêm một class Reward nhằm minh họa mang lại việc khối hệ thống tự động hóa khởi tạo ra đối tượng người dùng cho một class mới nhất nhưng mà không nhất thiết phải truyển vô qua chuyện hàm register.
class Reward { public function add(){ echo sprintf('%s add reward points'.PHP_EOL,static::class); } } class Cart { private Database $db; private Logger $log; private Notify $notify; private Reward $reward; public function __construct(Database $db,Logger $log,Notify $notify,Reward $reward){ $this->db= $db; $this->log= $log; $this->notify= $notify; $this->reward= $reward; echo sprintf('%s __construct '.PHP_EOL,static::class); } public function checkout(){ $this->db->save('order',[],[]); $this->log->write('checkout'); $this->notify->send('send gmail notify checkout'); $this->reward->add(); echo "------------------------------------------------------".PHP_EOL; } }
Các thông số được truyền vô trải qua Constructor
Đây là đoạn mã chủ yếu của chương trình:
$db = new MysqlDatabase(); App::register(Database::class,$db); $log = new FileLogger(); App::register(Logger::class,$log); $notify = new EmailNotify(); App::register(Notify::class,$notify); $cart = App::make(Cart::class); $cart->checkout();
Kết ngược hiển thị màn hình hiển thị là
DI register Database =>> MysqlDatabase DI register Logger =>> FileLogger DI register Notify =>> EmailNotify Cart __construct MysqlDatabase save data to tát table order FileLogger write log checkout EmailNotify send notify send gmail notify checkout Reward add reward points ------------------------------------------------------
Như tất cả chúng ta thấy, tuy vậy đi vào là Interface Database tuy nhiên đối tượng người dùng được vận dụng xử lý là MysqlDatabase. Tương tự động với FileLogger và EmailNotify.
Một xem xét nho nhỏ nữa là Reward tuy vậy tất cả chúng ta ko register tuy nhiên khối hệ thống vẫn tự động hóa khởi tạo ra và tiêm vô vào hàm khởi tạo ra của Cart.
Giải sử, giờ tất cả chúng ta mong muốn thay cho thay đổi 1 chút, chứ không dùng Mysql tất cả chúng ta dùng SQL Server thì nên cần sửa đổi như nào?
Sẽ không tồn tại bất kì đoạn mã này ở Cart.php nên thay cho thay đổi cả, giản dị và đơn giản là tất cả chúng ta tiếp tục khai báo lại (Register) lại nhưng mà thôi
$db = new MysqlDatabase(); App::register(Database::class,$db); $log = new FileLogger(); App::register(Logger::class,$log); $notify = new EmailNotify(); App::register(Notify::class,$notify); $cart = App::make(Cart::class); $cart->checkout(); $db = new SqlServerDatabase(); App::register(Database::class,$db); $cart = App::make(Cart::class); $cart->checkout();
Kết ngược tiếp tục là:
DI register Database =>> MysqlDatabase DI register Logger =>> FileLogger DI register Notify =>> EmailNotify Cart __construct MysqlDatabase save data to tát table order FileLogger write log checkoutEmailNotify send notify send gmail notify checkoutReward add reward points------------------------------------------------------ DI register Database =>> SqlServerDatabase Cart __construct SqlServerDatabase save data to tát table order FileLogger write log checkoutEmailNotify send notify send gmail notify checkoutReward add reward points------------------------------------------------------
Xem thêm: Tại sao nên sở hữu đôi giày Sneaker Nike chính hãng?
Các chúng ta thấy cơ, vô phiên checkout 1, thành phẩm vẫn như cũ là dùng MysqlDatabase tuy nhiên sau khoản thời gian thay cho thay đổi đối tượng người dùng thực ganh đua sang trọng Sqlserver nhưng mà ko cần thiết thay cho thay đổi bất kì loại code này vô cart cả. Hành động thực ganh đua ứng và được tiến hành.
Tổng kết
Tóm lại Inversion of Control hoặc Dependency Injection là nghệ thuật lập trình sẵn thông dụng bởi vì tính cơ động và hiệu suất cao nó tạo nên. Chúng được vận dụng trong không ít framework mã mối cung cấp với rất nhiều ngữ điệu lập trình sẵn không giống nhau. Không rằng đâu xa vời, ngay lập tức với framework Laravel, nếu như cầm được sự nắm rõ về Dependency Injection là chúng ta vẫn hiểu cốt lõi cơ bạn dạng phần vận hành của framework này. Hi vọng với nội dung bài viết này những các bạn sẽ hiểu rộng lớn về tiêm dựa vào na ná phần mềm nó vào vô thực tế
Bình luận