zoukankan      html  css  js  c++  java
  • PHP设计模式的六大设计原则

    PHP设计模式的六大设计原则

    1 简介

      软件设计最大的难题就是应对需求的变化,但是纷繁复杂的需求变化却是不可预料的.此时,我们可以通过六大设计原则良好的应对未来的变化.

    2 讲解

    2.1 单一职责原则(Single Responsibility Principle)

    一个类只负责一个职责

      简单的例子,用户发送一个请求到服务器,当我们所有的操作只用一个action类来完成

    <?php 
    $action = new action;
    $action->getResp();
    class action{
        public function getResp(){
            // 1检查路由
            // 2安全检查
            // 3检查缓存
            // 4查询数据库及返回
            // 5...
    ​
            echo 'hello world';
        }
    }

    那么当需求业务比如安全检查逻辑有变动时,我们都将修改这个类,繁琐而没有条理,极不容易维护.

    若我们把路由-安全-缓存等都封装成类,就仅在getResp()调用即可,简单优雅.

    2.2 开闭原则(Open Closed Principle)

    一个软件实体比如类-模块-函数,应该对扩展开放,对修改关闭

      小孩每天要做家庭作业

    <?php 
    class Child{
        public function doHomework( IHomework $homework ){
            $homework -> finHomework();
        }
    }
     
    interface IHomework{
        public function finHomework();
    }
     
    class Homework implements IHomework{
        private $work;
        public function __construct( $work ){    
            $this->work = $work;
        }
        public function getWork(){
            return $this->work;
        }
        public function finHomework(){
            echo "do homework : $this->work ".PHP_EOL.'<br/>';
        }
    }
    ​
    $xiaoming = new Child();
    $xiaoming -> doHomework( new Homework('math') );
    do homework : math 

      突然有一天老实宣布,临近期中考试了,作业要做两次.考完恢复成做一次.倘若我们直接修改Homework类的finHomework函数,虽然可以解决问题,但是期中考试结束后又需要把函数改回来.最好的解决办法就是利用开闭原则:

    class HomeworkTwice extends Homework{
        public function __construct( $work ){    
            parent::__construct($work);
        }
        public function finHomework(){
            $work = parent::getWork();
            echo "do homework : ".$work." 1".PHP_EOL.'<br/>';
            echo "do homework : ".$work." 2".PHP_EOL.'<br/>';
        }
    }
    ​
    $xiaoming = new Child();
    $xiaoming -> doHomework( new HomeworkTwice('math') );
    do homework : math 1
    do homework : math 2 

    2.3 里氏替换原则(Liskov Substitution Principle)

    所有引用基类的地方必须能透明地使用其子类的对象
    ​
    子类必须完全实现父类的方法,可以拓展自己的方法和属性.即子类可以扩展父类的功能,但不能改变父类原有的功能

      我们设计了Mp4类,它具有听歌和看视频的功能.

    interface IMp4{
        public function listenMusic();
        public function watchVedio();
    }
    ​
    class Mp4 implements IMp4{
        public function listenMusic(){
            echo ' listenMusic'.PHP_EOL.'<br/>';
        }
        public function watchVedio(){
            echo ' watchVedio'.PHP_EOL.'<br/>';
        }
    }
    ​
    class User1{
        public function litenM(IMp4 $mp4){
            echo 'user1';
            $mp4->listenMusic();
        }
        public function watchV(IMp4 $mp4){
            echo 'user1';
            $mp4->watchVedio();
        }
    }
    $user1 = new User1;
    $mp4 = new Mp4;
    $user1->litenM($mp4);
    $user1->watchV($mp4);
    user1 listenMusic
    user1 watchVedio 

      有一天我们要构建mp3的类,继续依照mp4的接口来生成类的话,会发现播放视频的功能用不了.

    class Mp3 implements IMp4{
        public function listenMusic(){
            echo ' listenMusic'.PHP_EOL.'<br/>';
        }
        public function watchVedio(){
            //不能播放视频
        }
    }
    $user1 = new User1;
    $mp3 = new Mp3;
    $user1->litenM($mp3);
    $user1->watchV($mp3);
    user1 listenMusic
    user1 

    此时我们可以构造IMp3接口来适应此种情况,我们还可以拓展处lookMini功能函数,符合里氏替换原则.

    interface IMp3{
        public function listenMusic();
    }
    class Mp3 implements IMp3{
        public function listenMusic(){
            echo ' listenMusic'.PHP_EOL.'<br/>';
        }
        public function lookMini(){
            echo ' lookMini'.PHP_EOL.'<br/>';
        }
    }

    2.4 迪米特法则(Law of Demeter)

    一个对象应该对其他对象保持最少的了解

      系统判定英雄是否赢取lol游戏,需要观察英雄完成三步:清理兵线-推塔-胜利.

    class Hero{
        //清理兵线
        public function cleanLine(){
            echo ' killed little soldiers '.PHP_EOL.'<br/>'; 
            return true;
        }
        //推塔
        public function pushtower(){
            echo ' destroyed their towers '.PHP_EOL.'<br/>'; 
            return true;
        }
        //胜利
        public function vitory(){
            echo ' victory '.PHP_EOL.'<br/>'; 
        }
    }
    class system{
        public function judgeVictory(Hero $hero){
            if($hero->cleanLine()){
                if($hero->pushtower()){
                    $hero->vitory();
                }
            }
        }
    }
    $system = new system;
    $jax = new Hero;
    $system->judgeVictory($jax);
    killed little soldiers
    destroyed their towers
    victory

    以上的Hero类中暴露了太多方法给system类,两者的耦合关系异常牢固.以下设计方法可以解决此问题.

    class Hero{
        //清理并线
        private function cleanLine(){
            echo ' killed little soldiers '.PHP_EOL.'<br/>'; 
            return true;
        }
        //推塔
        private function pushtower(){
            echo ' destroyed their towers '.PHP_EOL.'<br/>'; 
            return true;
        }
        //胜利
        private function vitory(){
            echo ' victory '.PHP_EOL.'<br/>'; 
        }
        //获取胜利
        public function getVictory(){
            if($this->cleanLine()){
                if($this->pushtower()){
                    $this->vitory();
                }
            }
        }
    }
    class player{
        public function playLol(Hero $hero){
            $hero->getVictory();
        }
    }
    $player = new player;
    $jax = new Hero;
    $player->playLol($jax);

    2.5 接口隔离原则(INterface Segregation Principle)

    类间的依赖应该建立在最小的接口上。

      有两个手机用户,用户1拿手机听歌,用户2拿手机打游戏接电话,场景实现如下:

    interface IPhone{
        public function call();
        public function playGame();
        public function listenMusic();
    }
    class Phone1 implements IPhone{
        public function call(){
            echo 'Phone1 call'.PHP_EOL.'<br/>';
        }
        public function playGame(){
            echo 'Phone1 playGame'.PHP_EOL.'<br/>';
        }
        public function listenMusic(){
            echo 'Phone1 listenMusic'.PHP_EOL.'<br/>';
        }
    }
    class Phone2 implements IPhone{
        public function call(){
            echo 'Phone2 call'.PHP_EOL.'<br/>';
        }
        public function playGame(){
            echo 'Phone2 playGame'.PHP_EOL.'<br/>';
        }
        public function listenMusic(){
            echo 'Phone2 listenMusic'.PHP_EOL.'<br/>';
        }
    }
    class User1{
        public function litenM(IPhone $phone){
            echo 'user1 use ';
            $phone->listenMusic();
        }
    }
    class User2{
        public function playG(IPhone $phone){
            echo 'user2 use ';
            $phone->playGame();
        }
        public function call(IPhone $phone){
            echo 'user2 use ';
            $phone->call();
        }
    }
    $phone1 = new Phone1;
    $user1 = new User1;
    $user1->litenM($phone1);
    ​
    $phone2 = new Phone2;
    $user2 = new User2;
    $user2->playG($phone2);
    $user2->call($phone2);

      我们发现,接口 IPhone 中出现的方法,不管依赖于它的类有没有作用,实现类的时候都要实现这些方法.若我们依据接口隔离原则,便可以解决以上问题.

    interface IlandlineTelephone{
        public function call();
    }
    interface IGameMachine{
        public function playGame();
    }
    interface IMp3{
        public function listenMusic();
    }
    /*interface IPhone extends IlandlineTelephone,IGameMachine,IMp3{
    }*/
    interface IPhone1 extends IMp3{
    }
    interface IPhone2 extends IlandlineTelephone,IGameMachine{
    }
    class Phone1 implements IPhone1{
        public function listenMusic(){
            echo 'Phone1 listenMusic'.PHP_EOL.'<br/>';
        }
    }
    class Phone2 implements IPhone2{
        public function call(){
            echo 'Phone2 call'.PHP_EOL.'<br/>';
        }
        public function playGame(){
            echo 'Phone2 playGame'.PHP_EOL.'<br/>';
        }
    }
    class User1{
        public function litenM(IPhone1 $phone){
            echo 'user1 use ';
            $phone->listenMusic();
        }
    }
    class User2{
        public function playG(IPhone2 $phone){
            echo 'user2 use ';
            $phone->playGame();
        }
        public function call(IPhone2 $phone){
            echo 'user2 use ';
            $phone->call();
        }
    }
    $phone1 = new Phone1;
    $user1 = new User1;
    $user1->litenM($phone1);
    ​
    $phone2 = new Phone2;
    $user2 = new User2;
    $user2->playG($phone2);
    $user2->call($phone2);

    2.6 依赖倒置原则(Dependence Inversion Principle)

    高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。

      以下是用户吃晚餐的场景:

    class Rice{
        public function taste(){
            echo ' rice is delicious'.PHP_EOL.'<br/>'; 
        }
    }
    class User{
        public function haveDinner(Rice $rice){
            $rice->taste();
        }
    }
    $user = new User;
    $rice = new Rice;
    $user->haveDinner($rice);
    soup is delicious 

    但是如果我们不止吃米饭,还喝汤呢,发现Rice并不适用了。我们引入一个抽象的接口Ifood,代表读物。让Mother类与接口Ifood发生依赖关系,而RiceSoup都属于食物的范畴,让他们各自都去实现IReader接口,这样就符合高层不应该依赖低层,应该依赖于接口的依赖倒置原则,修改后代码如下:

      用户吃完米饭后想要喝点汤,我们发现 haveDinner() 方法的依赖 Rice 不再适用.此时我们若依赖倒置,将haveDinner与更大范围的Ifood进行依赖,而Rice 和 Soup 实现Ifood接口,就可以解决所述问题.

    interface Ifood{
        public function taste();
    }
    class Rice implements Ifood{
        public function taste(){
            echo ' rice is delicious'.PHP_EOL.'<br/>'; 
        }
    }
    class Soup implements Ifood{
        public function taste(){
            echo ' soup is delicious'.PHP_EOL.'<br/>'; 
        }
    }
    class User{
        public function haveDinner(Ifood $food){
            $food->taste();
        }
    }
    $user = new User;
    $rice = new Rice;
    $soup = new Soup;
    $user->haveDinner($rice);
    $user->haveDinner($soup);
    rice is delicious
    soup is delicious 

    3 结尾

      六大设计原则的首字母联合起来为SOLID-稳定的(两个L合成一个).使用六大设计原则,可以建立灵活健壮的系统.

  • 相关阅读:
    使用redis作为缓存收集日志
    使用kafka作为缓存收集日志
    使用filebeat收集日志
    通过zabbix监控vCenter虚拟化
    zabbix配合grafana进行图形展示
    Filter学习
    spring框架学习(四)——注解方式AOP
    spring框架学习(三)——AOP( 面向切面编程)
    spring框架学习(二)——注解方式IOC/DI
    spring框架学习(一)——IOC/DI
  • 原文地址:https://www.cnblogs.com/bushuwei/p/10168911.html
Copyright © 2011-2022 走看看