Blogger Information
Blog 33
fans 0
comment 1
visits 43061
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
设计模式6大原则
萝卜温的博客
Original
884 people have browsed it
  • 迪米特原则:迪米特原则(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。
也就是返回值也可以是父子关系!


Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post