设计模式学习之七大原则

来源:互联网 发布:php网络验证源码 编辑:程序博客网 时间:2024/04/30 13:20

单一职责:

一个接口或类应该只有一种职能

一个类只完成它应该完成的职责

Demo:

class Phone{

    //拨号

public function dial(){

return "dialing....";

}

//挂断

public function hangup(){

return "hangup!!!";

}

//发送短信

public function send(){

return "send info!!!";

}

//接收短信

public function receive(){

return "received!!!";

}

}

//若修改接收方式的话则所有依赖Phone类的都需要修改

 

//改进后:按职责拆分为两个接口

interface IConnection{

public function dial();

public function hangup();

}

 

interface ICommunication{

public function send();

public function receive();

}

 

class Phone implements IConnection, {

public function dial(){

return "dialing....";

}

public function hangup(){

return "hangup!!!";

}

}

class Communication implements ICommunication{

public function send(){

return "send!!!";

}

public function receive(){

return "received!!!";

}

}

 

好处:

    降低类的复杂性,实现什么样的职责都有清晰的定义

    提高可读性

    提高可维护性

降低变更引起的风险,对系统扩展性和维护性很有帮助



开闭原则:开放扩展,关闭修改

为什么这么做?

在开发阶段,我们都知道,如果对一个功能进行扩展,如果只是一味地对方法进行修改,可能会造成一些问题,诸如 能会引入新的bug,或者增加代码的复杂度,对代码结构造成破坏、冗余,还需要重新进行全面的测试。那么该怎么解决这些问题?很简单,这就需要系统能够支持扩展,只有扩展性良好的系统,才能在不进行修改已有实现代码的基础上,引进新的功能。

    我们应该怎么做?

要做到开闭原则,就需要多使用抽象类或者接口,将相似的类进行抽象,将公有的功能引入到抽象类中,这样在进行扩展时,只需要依据抽象类,生成新的子类即可。



依赖注入原则:

要依赖于抽象,不要依赖于具体的实现(Spring面向接口编程

  为什么这么做?

减少类间的耦合性,提高代码的可读性和可维护性。

    我们应该怎么做?

a、每个类尽量都有接口和抽象类,或者抽象类和接口都有。

b、变量的表面类型尽量是接口或者是抽象类。

c、任何类都不应该从具体类派生。(但是在做二次开发的时候,我们无法获得高层代码的时候例外),规则不是绝对的。

d、尽量不要覆写基类已经实现好的方法。

 

Demo

比如有这么条需求,用户注册完成后要发送一封邮件,然后你有如下代码:

 

先有邮件类'Email.class.php'

 

class Mail{

    public function send()

    {

        /*这里是如何发送邮件的代码*/

    }

}

 

然后又注册的类'Register.class.php'

 

class Register{

    private $_emailObj;

 

    public function doRegister()

    {

        /*这里是如何注册*/

 

        $this->_emailObj = new Mail();

        $this->_emailObj->send();//发送邮件

    }

}

 

然后开始注册

 

include 'Mail.class.php';

include 'Register.class.php';

$reg = new Register();

$reg->doRegister();

 

看起来事情很简单,你很快把这个功能上线了,看起来相安无事... xxx天过后,产品人员说发送邮件的不好,要使用发送短信的,然后你说这简单我把'Mail'类改下...

 

又过了几天,产品人员说发送短信费用太高,还是改用邮件的好...  此时心中一万个草泥马奔腾而过...

 

这种事情,常常在产品狗身上发生,无可奈何花落去...

 

以上场景的问题在于,你每次不得不对'Mail'类进行修改,代码复用性很低,高层过度依赖于底层。那么我们就考虑'依赖倒置原则',让底层继承高层制定的接口,高层依赖于接口。

 

interface Mail

{

    public function send();

}

 

class Email implements Mail()

{

    public function send()

    {

        //发送Email

    }

}

 

class SmsMail implements Mail()

{

    public function send()

    {

        //发送短信

    }

}

 

class Register

{

    private $_mailObj;

 

    public function __construct(Mail $mailObj)

    {

        $this->_mailObj = $mailObj;

    }

 

    public function doRegister()

    {

        /*这里是如何注册*/

        $this->_mailObj->send();//发送信息

    }

}

 

下面开始发送信息

 

/* 此处省略若干行*/

$reg = new Register();

$emailObj = new Email();

$smsObj = new SmsMail();

 

$reg->doRegister($emailObj);//使用email发送

$reg->doRegister($smsObj);//使用短信发送

/* 你甚至可以发完邮件再发短信*/

 

上面的代码解决了'Register'对信息发送类的依赖,使用构造函数注入的方法,使得它只依赖于发送短信的接口,只要实现其接口中的'send'方法,不管你怎么发送都可以。上例就使用了"注入 "这个思想,就像注射器一样将一个类的实例注入到另一个类的实例中去,需要用什么就注入什么。当然"依赖倒置原则 "也始终贯彻在里面。"注入 "不仅可以通过构造函数注入,也可以通过属性注入,上面你可以可以通过一个"setter"来动态为"mailObj"这个属性赋值。



里式替换原则:

里氏替换原则是对类继承的一种约束。对里氏替换原则有两种理解:

不能随便去继承不合适的,有多余方法或者属性的类。(例子1

子类可以扩展父类的功能,但不能改变父类原有的功能。(例子2

 

为什么这么做?

采用里氏替代原则可以增强程序的健壮性,版本升级的时候可以保持非常好的兼容性,即使增加子类,原有的子类也可以继续运行。

    我们应该怎么做?

在引用基类的地方就能引用子类实现

 

//例子1

class Bird{

    protect function fly(){

    }

}

//翠鸟

class KingFisher extends Bird{

}

 

//鸵鸟

class Ostrich extends Bird{

    //鸵鸟不会飞啊

}

 

 

//例子2

class A{

    protect function add($a, $b){

        return $a + $b;

    }

}

//重载

class B extends A{

    protected function add($a, $b){

        return $a + $b + 100;

    }

}

看了第二个例子,有人会说那岂不是和重载矛盾了。初看是有点,但仔细理解,并不矛盾,我们可以这样处理矛盾:

//例子2

class A{

    protect function add($a, $b){

        return $a + $b;

    }

}

//重载

class B extends A{

    protected function add($a, $b, $c){

        return isset($c) ? $a + $b + 100 : $a + $b;

    }

}

 

 

里氏替换原则包含一下几个隐藏含义:

    子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。

    子类中可以增加自己特有的方法。

    当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。

 

    当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。



迪米特原则:

一个对象应该对其他对象了解最少。

具体体现:

① 在类的划分上,应该创建有弱耦合的类;

② 在类的结构设计上,每一个类都应当尽量降低成员的访问权限;

③ 在类的设计上,只要有可能,一个类应当设计成不变类;

④ 在对其他类的引用上,一个对象对其它对象的引用应当降到最低;

⑤ 尽量降低类的访问权限;

⑥ 谨慎使用序列化功能(类或接口在客户端变更,却未在服务端同步更新,引发序列化失败,,项目管理易疏忽);

⑦ 不要暴露类成员,而应该提供相应的访问器(属性)

 

为什么这么做?

 降低类之间的耦合。

 

    我们应该怎么做?

在应用中最直接的实现就是在两个类中间建一个中介类。但是这样可能会造成中介类的澎爆。

 

迪米特法则主要运用在观察者模式和中介者模式中

Demo

class Teacher {

//老师对学生发布命令,清一下女生

public function commond(GroupLeader $groupLeader){

//初始化女生

for($i=0; $i<20; $i++){

$listGirls[] = new Girl();

}

//告诉体育委员开始执行清查任务

$groupLeader -> countGirls($listGirls);

}

}

class GroupLeader {

//有清查女生的工作

public function countGirls($listGirls = array()){

echo '女生的数量是:' . sizeof($listGirls);

}

}

class Girl {

 

}

class Client {

public static function doing() {

$teacher= new Teacher();

//老师发布命令

$teacher -> commond(new GroupLeader());

}

}

Client :: doing();

 

//改进后

class Teacher {

 

public function commond(GroupLeader $groupLeader){

//告诉体育委员开始执行清查任务

$groupLeader -> countGirls();

}

}

 

class GroupLeader {

private $_listGirls = array();

//传递全班的女生

public function __construct($listGirls){

$this -> _listGirls = $listGirls;

}

//有清查女生的工作

public function countGirls(){

echo "女生数量是:" . sizeof($this -> _listGirls);

}

}

 

class Girl {

 

}

 

class Client {

public static function doing() {

//初始化女生

for($i=0; $i<20; $i++){

$listGirls[] = new Girl();

}

$teacher= new Teacher();

//老师发布命令

$teacher -> commond(new GroupLeader($listGirls));

}

}

Client :: doing();


接口隔离原则:

一个接口或者类应该拥有尽可能少的行为(那么,什么叫尽可能少?就是少到恰好能完成它自身的职责)

不要迫使实现接口的类去实现和该类无关的方法。

 

   为什么这么做?

避免让接口的实现类实现一些不必要的功能

    我们应该怎么做?

建立单一的接口,不要建立臃肿的庞大的接口,也就是说接口的方法尽量少。

 

Demo:

 

interface IBird{

    public function walk();

    public function chirp();

    public function fly();

}

 

class Ostrich implements IBird{

    //鸵鸟实现,出问题了,不会飞

}

接口隔离原则看起来确实很简单,但要注意在拆分接口的时候的粒度,不能太细,例如这个例子不能把每个动作都写个接口吧

 


优先使用组合而不是继承原则:

组合Demo

class person{

public $name;

public $gender;

public function say(){

echo $this->name," tis ",$this->gender,"rn";

}

}

class family{

public $people;

public $location;

public function construct($p,$loc){

$this->people=$p;

 

$this->location=$loc;

}

}

$student=new person();

$student->name='Tom';

$student->gender='male';

$student->say();

$tom=new family($student,'peking');

 

以上代码中,定义了两个类,一个是person,一个是family;在family类中创建person类中的对象,把这个对象视为family类的一个属性,并调用它的方法处理问题,这种复用方式就叫“组合”。




0 0
原创粉丝点击