设计模式六大原则详解

一:设计模式六大原则

1. 单一职责原则(single Responsibility Principle)

Single-Responsibilitiy Principle(SRP):对一个类而言,应该仅有一个引起它变化的原因。如果存在多于一个动机去改变一个类,那么这个类就具有多于一个的职责,就应该把多余的职责分离出去,再去创建一些类来完成每一个职责。

举个例子:一个人身兼数职,而这些事情相关性不大,甚至有冲突,那他就无法很好的解决这些问题职责,应该分到不同的人身上去做。

在这里插入图片描述

<?php
class Animal
{
    public function breathe($animal)
    {
        echo $animal . "在空气中呼吸<br>";
    }
}

class Program
{
    public function main()
    {
        $animal = new Animal();
        $animal->breathe("牛");
        $animal->breathe("羊");
        // 鱼是在水中呼吸,因此这样不妥
        $animal->breathe("鱼");
    }
}


#------------------- 修正 ---------------------#
class Aquatic
{
    public function breathe($animal)
    {
        echo $animal . "在水中呼吸<br>";
    }
}

class Program2
{
    public function main()
    {
        $animal = new Animal();
        $animal->breathe("牛");
        $animal->breathe("羊");
        // 鱼是在水中呼吸
        $aquatic = new Aquatic();
        $aquatic->breathe("鱼");
    }
}

单一职责原则是实现高内聚低耦合的最好方法,没有之一。

2. 里氏代换原则(Liskov Substitution Principle)

Liskov Substitution Principle:子类可以扩展父类的功能,但是不能改变父类原有的功能。

在第一条原则开放封闭原则中,主张“抽象”和“多态”。维持设计的封装性“抽象”是语言提供的功能,“多态”由继承语意实现。因此如何去度量继承关系中的质量?

答案是:继承必须明确确保超类(父类)所拥有的性质在子类中仍然成立。

在面向对象的思想中,一个对象就是一组状态和一系列行为的组合体。状态是对象的内在特性,行为是对象的外在特性。LSP表述的就是在同一继承体系中的队形应该具有共同的行为特征。

<?php
class A
{
    public function subtract($a, $b)
    {
        return $a - $b;
    }

    public function quadrature($a, $b)
    {
        return $this->subtract($a, $b) * 100;
    }
}

// 这种是修改,而非拓展
class B extends A
{
    public function subtract($a, $b)
    {
        return $a + $b;
    }

    public function quadrature($a, $b)
    {
        return $this->subtract($a, $b) * 100;
    }
}

#------------------拓展--------------------#
abstract class D
{
    public abstract function subtract($a, $b);
    public function quadrature($a, $b)
    {
        return $this->subtract($a, $b) * 100;
    }
}
// 通过子类继承实现抽象方法达到拓展目的
class E extends D
{
    public function subtract($a, $b)
    {
        return $a + $b;
    }
}

class F extends D
{
    public function subtract($a, $b)
    {
        return $a - $b;
    }
}

3. 依赖倒置原则(Dependence Inversion Principle)

Dependence Inversion Principle(DIP):是一个类与类之间的调用规则。这里的依赖就是代码中的耦合。高层模块不应该依赖底层模块,二者都应该依赖其抽象了;抽象不依赖细节;细节应该依赖抽象。接口编程。
在这里插入图片描述

主要思想就是:如果一个类中的一个成员或者参数成为一个具体的类型,那么这个类就依赖这个具体类型。如果在一个继承结构中,上层类中的一个成员或者参数为一个下层类型,那么就是这个继承结构高层依赖底层,就要尽量面向抽象或者接口编程。

举例:存在一个Driver类,成员为一个Car对象,还有一个driver()方法,Car对象中有两个方法start()与stop()。显然Driver依赖Car,也就是说Driver类调用了Car类中的方法。但是当增加Driver类对于Bus类的支持时(司机有需要开公交车),就必须更改Driver中的代码,就破坏了开放封闭原则。根本原因在于高层的的Driver类与底层的Car类仅仅的耦合在一起的。解决方法之一就是:对Car类和Bus类进行抽象,引入抽象类Automoble。而Car和Bus则是对Automobile的泛化。
在这里插入图片描述

经过这样的改造发现,原本的高层依赖底层,变成了高层与底层同时依赖抽象。这就是依赖倒转原则的本质。

优点:

  • 低层模块尽量都要有抽象类或接口,或者两者都有
  • 变量的声明类型尽量是抽象类或接口
  • 使用继承时遵循里氏替换原则
<?php
/*
 * 依赖倒置原则
 */
class Book
{
    public function getContent()
    {
        return "在很久很久以前";
    }
}
class Newspaper
{
    public function getContent()
    {
        return "php是最好的语言";
    }
}
class Mother
{
    public function narrate(Book $book)
    {
        echo $book->getContent();
    }
}

$m = new Mother();
$m->narrate(new Book());

#----------------改正----------------#
interface IReader
{
    public function getContent();
}
class Bookl implements IReader
{
    public function getContent()
    {
        return "在很久很久以前";
    }
}

class Newspaperl implements IReader
{
    public function getContent()
    {
        return "php是最好的语言";
    }
}

class Motherl
{
    public function narrate(IReader $iRead)
    {
        echo $iRead->getContent();
    }
}

$ml = new Motherl();
$ml->narrate(new Bookl());
$ml->narrate(new Newspaperl());

4. 接口隔离原则(Interface Segregation Principle)

接口隔离原则(Interface Segregation Principle):用于恰当的划分角色和接口,具有两种含义:1、用户不应该依赖它不需要的借口;2、类间的依赖关系应该建立在最小的的接口上。
在这里插入图片描述

将这两个定义概括为一句话:建立单一接口,代替庞大臃肿的接口。通俗来说就是:接口尽量细化,同时保证接口中的方法尽量的少。一个接口中包含太多的行为时,会导致它们与客户端的不正常依赖关系,要做的就是分离接口,从而实现解耦。

回到上述的单一职责原则,要求行为分离接口接口细化,感觉有些相同。但实际上,单一职责原则要求类与接口的职责单一,注重的是职责,没有要求接口尽量的少。

在接口隔离原则中,要求尽量使用多个专门的接口。专门的接口也就是提供给多个模块的接口。提供给几个模块就应该有几个接口,而不是建立一个臃肿庞大的接口,所有的模块都可以访问。

但是接口的设计是有限度的。接口的设计粒度越小系统越灵活,这是事实,但是接口太多这也就使得结构复杂,维护难度大。因此实际中,怎样把握就靠开发的经验和常识了。

<?php
/*
 * 接口隔离原则
 */
interface I
{
    public function method1();
    public function method2();
    public function method3();
    public function method4();
    public function method5();
}

class A
{
    public function depend1(I $i)
    {
        $i->method1();
    }
    public function depend2(I $i)
    {
        $i->method2();
    }
    public function depend3(I $i)
    {
        $i->method3();
    }
}

class B implements I{

    public function method1()
    {
    }

    public function method2()
    {
    }

    public function method3()
    {
    }

    public function method4()
    {
    }

    public function method5()
    {
    }
}

class C
{
    public function depend1(I $i)
    {
        $i->method1();
    }
    public function depend2(I $i)
    {
        $i->method2();
    }
    public function depend3(I $i)
    {
        $i->method3();
    }
}

class D implements I{

    public function method1()
    {
    }

    public function method2()
    {
    }

    public function method3()
    {
    }

    public function method4()
    {
    }

    public function method5()
    {
    }
}


$a = new A();
$a->depend1(new B());
$a->depend2(new B());
$a->depend3(new B());

$c = new C();
$c->depend1(new D());
$c->depend2(new D());
$c->depend3(new D());

#------------------修正-------------------#
interface I1
{
    public function method1();
}
interface I2
{
    public function method2();
    public function method3();
}
interface I3
{
    public function method4();
    public function method5();
}
class Al
{
    public function depend1(I1 $i)
    {
        $i->method1();
    }
    public function depend2(I2 $i)
    {
        $i->method2();
    }
    public function depend3(I2 $i)
    {
        $i->method3();
    }
}
class Bl implements I1, I2{
    public function method1()
    {
    }

    public function method2()
    {
    }

    public function method3()
    {
    }
}
class Cl
{
    public function depend1(I1 $i)
    {
        $i->method1();
    }
    public function depend2(I3 $i)
    {
        $i->method4();
    }
    public function depend3(I3 $i)
    {
        $i->method5();
    }
}
class Dl implements I1, I3{
    public function method1()
    {
    }

    public function method4()
    {
    }

    public function method5()
    {
    }
}
$a = new Al();
$a->depend1(new Bl());
$a->depend2(new Bl());
$a->depend3(new Bl());

$c = new Cl();
$c->depend1(new Dl());
$c->depend4(new Dl());
$c->depend5(new Dl());

5. 迪米特原则(Law of Demeter)

Law of Demeter(最小知识原则):一个对象应该对其他对象有最少的了解。通俗来说就是,一个类对自己需要耦合或者调用的类知道的最少,你类内部怎么复杂,我不管,那是你的事,我只知道你有那么多公用的方法,我能调用。

迪米特原则不希望类与类之间建立直接的接触。如果真的需要有联系,那么就通过它们的友元类来传达。举例来说:你需要买房子了,现在存在三座合适的楼盘A,B,C,但是你不必直接去楼盘买楼,而是在售楼处去了解情况。这样就减少了你(购房者)与楼盘两个类之间耦合。

但是应用迪米特原则很可能会造成一个后果:系统会存在大量的中介类,这些类(如上面的售楼处类)之所以存在是为了传递类之间的相互调用关系,这就一定会程度上增加了系统的复杂度。

迪米特原则核心观念就是:类间解耦,弱耦合。

<?php
/*
 * 迪米特原则(最少知道原则)
 */
class SubCompanyManager
{
    public function getAllEmployee()
    {
        $arr = [];
        for($i = 0; $i < 40; $i++) {
            $arr[$i] = $i;
        }
        return $arr;
    }
}

class CompanyManager
{
    public function getAllEmployee(SubCompanyManager $sub)
    {
        for ($i = 0; $i < 30; $i++)
        {
            echo "总公司" . $i . "<br>";
        }
        $arr = $sub->getAllEployee();
        foreach ($arr as $v) {
            echo "分公司" . $v . "<br>";
        }
    }
}

$sub = new SubCompanyManager();
$class = new CompanyManager();
$class->getAllEmployee($sub);

#-------------------改正----------------------#
class SubCompanyManagerl
{
    public function getAllEmployee()
    {
        for($i = 0; $i < 40; $i++) {
            echo "分公司" . $i . "<br>";
        }
    }
}

class CompanyManagerl
{
    public function getAllEmployee(SubCompanyManagerl $sub)
    {
        for ($i = 0; $i < 30; $i++)
        {
            echo "总公司" . $i . "<br>";
        }
        $sub->getAllEployee();
    }
}

$subl = new SubCompanyManagerl();
$classl = new CompanyManagerl();
$classl->getAllEmployee($subl);

6. 开闭原则(open close Principle)

Open-Close Principle(OCP):一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。目的就是保证程序的扩展性好,易于维护和升级。

开闭原则被称为面向对象设计的基石,实际上,其他原则都可以看作是实现开闭原则的工具和手段。意思就是:软件对扩展应该是开放的,对修改是封闭的,通俗来说就是,开发一个软件时,应该对其进行功能扩展,而在进行这些扩展时,不需要对原来的程序进行修改。

好处是:软件可用性非常灵活,扩展性强。需要新的功能时,可以增加新的模块来满足新需求。另外由于原来的模块没有修改,所以不用担心稳定性的问题。

如果有兴趣了解更多相关内容,欢迎来我的个人网站看看:瞳孔的个人网站

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值