博客列表 >设计模式6大原则

设计模式6大原则

萝卜温的博客
萝卜温的博客原创
2018年07月23日 22:08:15883浏览
  • 迪米特原则:迪米特原则(Law of Demeter,LoD),也叫最少知识原则(Low knowledge Principle,LKP):一个对象应该对其他对象有最少的了解。也可以解释为只和朋友类进行通信,所谓朋友类就是直接和一个类产生耦合关系的类,而这个朋友类就是实现两个类之间低耦合的关键!

  • 单一职责原则(Single Responsibility Principle)–SRP:There should never be more than one reason for a class to change -- 应该有且仅有一个原因引起类的变更,也就是说一个类应该只负责一种特定的功能!好处:便于维护,稳定性高,简化代码逻辑,代码易于理解!例如下面的代码:

/***情景 1:动物用通过空气呼吸*****/
class Animal {
        private $name;
    public function __construct ($name) {
        $this -> name = name;
    }
    public function breath () {
        echo $this -> name . "呼吸空气!";
    }
}
/***情景 2:有一天,突然发现鱼呼吸水的*****/
//扩展方式1:方法体层面,这个严重违反了单一责任原则,因为能够导致改代码的情况是所有动物都换了呼吸方式,这里只是部分水生动物的呼吸方式换了
class Animal {
    private $name;
    public function __construct ($name) {
        $this -> name = name;
    }
    public function breath () {
        if ($this -> name == "鱼") {
            echo $this -> name . "呼吸水!";
        } else {
            echo $this -> name . "呼吸空气!";
        }
    }
}
//扩展方式2:方法层面,一定程度违反单一职责原则
class Animal {
    private $name;
    public function __construct ($name) {
        $this -> name = name;
    }
    public function breath () {
                $this -> name . "呼吸空气!";
    }
    public function breath () {
                $this -> name . "呼吸水!";
        }    
}
//扩展方式3:类层面,符合单一职责原则
class luSheng {
    private $name;
    public function __construct ($name) {
        $this -> name = name;
    }
    public function breath () {
                $this -> name . "呼吸空气!";
    }
}
class shuiSheng {
    private $name;
    public function __construct ($name) {
        $this -> name = name;
    }
    public function breath () {
                $this -> name . "呼吸水!";
    }
}
  • 依赖倒置原则(Dependence Inversion Principle ,DIP)定义如下:High level modules should not depend upon low level modules,Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstracts. -- 高层次模块不能依赖于低层次模块,高低层次模块都应该依赖于抽象们;抽象不应该以来与实现的细节,实现细节应该依赖于抽象!总体概括为高低层次模块+实现细节都应该依赖于抽象们!DIP是面向接口编程,基本上面向接口编程都是符合dip原则的!注:低层次模块是高层次模块的组成部分 -- 高层次模块属于一个大的逻辑,而低层次模块属于颗粒原子逻辑,大逻辑由颗粒原子逻辑组成!代码例子如下:

//依赖注入的三种方式:构造方法注入,setter注入,接口注入

/***情景1:小红看红楼梦,看了一会,想看个小说*****/
class XiaoHong{
    private $book;
    public function __construct (HongLouMeng $book) {
        $this -> book = $book;
    }
    public function read () {
        $this -> book -> read();
    }
}
class HongLouMeng {
        public function read () {
            echo "我在看红楼梦,四大名著之一喔!";
        }
}
class Novel {
    public function read () {
        echo "看会小说,斗破苍穹!";
    }
}
//注:上面是一个强依赖,因为小红只能看《红楼梦》,不能看其他书!
//另外,XiaoHong(高层次模块)依赖于HongLouMeng(低层次模块),因此违反了依赖倒置原则!
//功能:声明读物接口
interface Read {
    public function read();
}
//功能:声明阅读者接口,接口依赖注入
interface Reader {
    public function read (Read $book);
}
class XiaoHong implements Reader {
    private $book;

    public function read (Read $book) {
        $book -> read();
    }
}
class HongLouMeng implements Read {
        public function read () {
            echo "我在看红楼梦,四大名著之一喔!";
        }
}
class Novel implements Read {
    public function read () {
        echo "看会小说,斗破苍穹!";
    }
}
//上面的设计就可以读到小说以及所有实现了 Read 接口的书了,真正从抽象层面实现了依赖
  • 接口隔离原则:客户端只依赖于它需要的接口,多余的接口都不要,这样子的话就要求接口设计尽量细化!

/*****盗用别人的例子,因为实在太好了****/
/***情景1:星探要发掘美女啦,当代美女的标准是 脸漂亮、好身材、好脾气 ***/
interface IPretty {
    public function IsGoodLooking ();
    public function IsGoodBody ();
    public function IsGoodTemper ();
}
abstract class Searcher {
    public setStandardPretty (IPretty $girl);
    public showStandardPretty ();
}
class PrettyGirl implements IPretty {
    public $name;
    public function __construct ($name) {
        $this -> name = $name;
    }
    public function IsGoodLooking () {
        echo $this -> name .  "脸很漂亮!";
    }
    public function IsGoodBody () {
        echo $this -> name . "身材火爆!";
    }
    public function IsGoodTemper () {
        echo $this -> name . "脾气很好!";
    }
}
class MySearcher extends Searcher {
        private $girl;
        public function setStandardPretty (IPretty $girl) {
            $this -> girl = $girl;
        }
        public function showStandardPretty () {
            echo $this -> name . "的个人情况如下:";
            $this -> girl -> IsGoodLooking ();
            $this -> girl -> IsGoodBody();
            $this -> girl -> IsGoodTemper();
        }
} 
$girl = new PrettyGirl("玛丽");
$searcher = new MySearcher();
$searcher -> setStandardPretty ($girl);
$searcher -> showStandardPretty ();

/***情景2:出现了另外一种美女的标准:只需要好脾气就行了,怎么办呢,那个接口 IPretty 根本不能满足****/
interface IPretty1 {
    public function IsGoodLooking ();
    public function IsGoodBody ();
}
interface IPretty2 {
    public function IsGoodTemper ();
}
interface IPretty extends IPretty1, IPretty2 {
}
abstract class Searcher {
    public setStandardPretty (IPretty $girl);
    public showStandardPretty ();
    public setTemperPretty (IPretty2 $girl);
    public showTemperPretty ();
}
class PrettyGirl implements IPretty {
    public $name;
    public function __construct ($name) {
        $this -> name = $name;
    }
    public function IsGoodLooking () {
        echo $this -> name .  "脸很漂亮!";
    }
    public function IsGoodBody () {
        echo $this -> name . "身材火爆!";
    }
    public function IsGoodTemper () {
        echo $this -> name . "脾气很好!";
    }
}
class TemperPrettyGirl implements IPretty2 {
    public $name;
    public function __construct ($name) {
        $this -> name = $name;
    }
    public function IsGoodTemper () {
        echo $this -> name . "脾气很好!";
    }
}
class MySearcher extends Searcher {
        private $girl;
        private $temper;
        public function setStandardPretty (IPretty $girl) {
            $this -> girl = $girl;
        }
        public function setTemperPretty (IPretty2 $girl) {
            $this -> temper = $girl;
        }
        public function showStandardPretty () {
            echo $this -> name . "的个人情况如下:";
            $this -> girl -> IsGoodLooking ();
            $this -> girl -> IsGoodBody();
            $this -> girl -> IsGoodTemper();
        }
        public function showTemperPretty () {
            echo $this -> name . "的个人情况如下:";
            $this -> girl -> IsGoodTemper();
        }
} 
$girl = new PrettyGirl("玛丽");
$goodTemper = new TemperPrettyGirl("苏玛丽");
$searcher = new MySearcher();
$searcher -> setStandardPretty ($girl);
$searcher -> showStandardPretty ();
$searcher -> setTemperPretty ($goodTemper);
$searcher -> showTemperPretty ();
//上面代码将接口细化了,所以可以灵活地组合,更加方便扩展
  •  开闭原则:Software entities like classes,modules and functions should be open for extension but closed for modifications -- 软件实体,例如类、模块和函数,应该通过扩展来适应变化,不要通过修改代码来适应变化。例如下面的代码:

/***情景1:一个书店卖一本书,正常售卖,封装好的代码如下*****/
interface IBook {
    public function getName ();
    public function getPrice ();
    public function getAuthor ();
}
class NovelBook implements IBook {
    protected $name;
    protected $price;
    protected $author;
    
    public function __construct ($name, $price, $author) {
        $this -> name = $name;
        $this -> price = $price;
        $this -> author = $author;
    }
    
    public function getName () {
        return $this -> name;
    }
    
    public function getPrice () {
        return $this -> price;
    }
    
    public function getAuthor () {
        return $this -> author;
    }
}

/***情景2:书店活动,该书需要打折,怎么办呢,有如下三种方式****/
//方式1:在接口 IBook 中添加 public function getOffPrice ();方法,这样子做就是在改代码,
//同时还需要修改NovelBook中的代码,如果还有其他书实现了这个接口的话也是需要修改它们当中的代码,所以方式1不可取!
//方式2:在 NovelBook 这个实现类中修改代码,修改 getPrice() 或者添加一个方法 getOffPrice (),这样子是可行,但是
//并不是最优的方式
//方式3:继承 NovelBook ,产生一个子类,然后在覆盖掉那个 getPrice () 方法,通过扩展方式去实现,
//对现有底层代码没有影响,在高层次模块中进行代码修改!
class OffNovelBook extends NovelBook {
    public function __construct ($name, $price, $author) {
        parent::construct ($name, $price, $author);
    }
    public function getPrice () {
        if ($this -> price > 40) {
            return $this -> price * 0.9;
        } else {
            return $this -> price * 0.8;
        }
    }
}
//注:变化有逻辑变化和子模块变化两种:
//前者是一段逻辑的变化,但是结果的处理方式不变,这样子可以直接修改代码!
//后者是模块的变化,这样子可能会影响到很多相关联的模块,尤其是底层模块发生变化时!
  • 里氏替换原则:Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it. -- 将一段代码中的父类对象替换为该类的子类对象,代码仍然可以顺利正确地执行,这样子就叫做符合里氏替换原则。这在数学上类比集合的包含关系,e 属于集合 A,对于集合 B 包含集合 A,e 属于集合 B!通俗来说,子类继承了父类中的一切,父类能够做到的,子类也可以;相反,子类的个性化操作,父类没有办法做到!

里氏替换原则有4个规范:
1.子类必须完全实现父类的方法。如果子类不能完整地实现父类的方法,或者父类的一些方法在子类中已经发生畸变,
则建议断开继承关系,采用依赖,聚集,组合等关系代替继承。代码走起:
abstract class AbstractGun{
   public abstract function shoot();
}
class Handgun extends AbstractGun{
   public function shoot(){
      echo "手枪射击...";
   }
}
class Rifle extends AbstractGun{
   public function shoot(){
      echo "步枪射击...";
   }
}
class Cachine extends AbstractGun{
   public function shoot(){
      echo "机枪射击...";
   }
}
class Soldier{  
    private $gun;  
    //这里就是使用父类的地方,可以使用子类(具体实现类对象)
    public function setGun(AbstractGun $gun){    
        $this -> gun = $gun;
    }  
    public function killEnemy(){
        echo "士兵开始射击...";    
        $this -> gun -> shoot();
    }
}
$soldier = new Soldier();     
//设置士兵手握手枪
$soldier -> setGun(new Handgun());
$soldier -> killEnemy();     
//设置士兵手握步枪
$soldier -> setGun(new Rifle());
$soldier -> killEnemy();     
//设置士兵手握机枪
$soldier -> setGun(new Cachine());
$soldier -> killEnemy();

2.子类可以有自己的个性
class Aug extends Rifle {
    public function ZoomOut () {
        echo "通过放大镜观察。。。";
    }
    public function shoot () {
        echo "AUG射击。。。";
    }
}
class Snipper {
    public function killEnemy(AUG $aug){
     $aug -> zoomOut();
     $aug -> shoot();
    }
}
$snipper = new Snipper();
$snipper -> killEnemy(new AUG());
输出:
通过放大镜观察。。。AUG射击。。。
$snipper = new Snipper();
$snipper -> killEnemy(new Rifle());
输出:
报错,报ZoomOut方法不存在。。。

3.覆盖或实现父类的方法时输入参数可以被放大(放大的意思是范围更加广泛,也就是祖先类;缩小就是更加精确分类到具体类)
需要用到 Java 来阐述,因为PHP中是不同的机制!
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class Father{  
    public Collection doSomething(HashMap map){
        System.out.println("父类被执行");     
        return map.values();    
    }
}
public class Son extends Father{
  //这里其实相当于重载了,所以并没有覆盖掉父类的doSomething方法
  public Collection doSomething(Map map){
     System.out.println("子类被执行");     
     return map.values();
  }
}
public class Client{   
   public static void main(Strings[] args){       
       //父类存在的地方,子类应该可以存在
       //Father father = new Father();
       Son father = new Son();  
       //这两个都会输出 "父类被执行" 这句话,因为这是重载,如果需要输出 "子类被执行" 这句话,则需要完全覆盖掉
       //父类中doSomething方法,参数类型一模一样才行!
       HashMap map = new HashMap();
       father.doSomething(map);
   }
} 
       
4.覆盖或实现父类的方法时输出结果可以被缩小
父类的一个方法的返回值是一个类型T,子类的相同方法的返回值为S,那么里氏替换原则就要求S必须小于等于T。
也就是返回值也可以是父子关系!


声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议