不一样的面向对象(二)

书旅

设计模式六大原则

单一职责原则(SRP)

定义:一个类应该只有一个引起它变化的原因

假设有一个类N,它负责两个职责,Z1和Z2。假设职责Z1发生了改变,就需要修改N类,而修改了N类就可能会导致本来可以正常运行的Z2不能正常运行了

在系统中,一个类承担的职责越多,那么它被复用的可能性肯定是越小的。承担的职责越多,很容易将这些职责耦合在一起,这时,一旦某个职责发生了变化,就可能会影响到其它职责的正常运行。所以,可以将不同的职责进行分离,封装到不同的类中

单一职责原则可以说是实现高内聚、低耦合的一个指导方针。show me code


<?php

class BaiLi

{

public function flash() //闪现

{

echo "闪现!".PHP_EOL;

}

public function pushVision() //插眼

{

echo "我插了个眼".PHP_EOL;

}

public function sniper() //狙击

{

echo "我狙了一枪".PHP_EOL;

}

public function backToCity() //回城

{

echo "状态不好,回城".PHP_EOL;

}

}

写了一个百里的类,类的设计看上去好像是没什么问题,类中都是这个英雄的各种技能和操作。单一职责原则要求一个类只有一个原因引起它的变化,也就是它应该只负责一件事。而这里,它其实负责了两件事,或者说三件事,闪现、回城这算是公共技能,插眼、狙击算是该英雄特有的技能。如果该英雄要进行改版,需要更改百里的特有技能,那这里的功能技能可能就会受到影响。公共技能实际上不应该因为任何英雄的改版而受影响的,所以应该将这两个职责拆开,拆成两个类,分别进行封装

例子可能不是特别恰当,大概是这么个意思。假设有一个登录的类(Login),它里边有处理表单数据功能(dealForm)、表单数据校验功能(checkForm)、登录功能(login)、获取用户信息功能(getUserInfo)等

这个类其实就没有遵循单一职责原则,用单一职责原则进行重构,应该将处理表单数据(dealForm)、表单数据校验(checkForm)封装到一个表单处理类中(Form),将获取用户信息(getUserInfo)放到一个User类中

开放封闭原则(OCP)

定义:软件中的对象(类、模块、函数等)应该对扩展是开放的,但是,对修改是封闭的

对扩展开放的意思是,我们可以随便增加新功能,而对原有功能不会产生任何修改。也就是说,软件中的对象尽量应该在不修改原有代码的情况下进行扩展

工作中我们其实大部分时间都是在维护代码,或因需求的变更,或因升级。如果我们在原有的代码基础上进行修改,那么就可能对老的代码造成影响。时间一久,我们就不得不对系统进行重构,而且还需要把原有的代码再重新测一遍。那么如果有需求变更或系统升级,我们是通过扩展来实现新的需求,那么就不会对历史的逻辑和代码造成影响

所以我们在设计代码的时候就应该考虑,如何做到对扩展开放、对修改封闭?

通常的方式就是抽象一个接口或者抽象类,定义公共的方法,从而实现方便的扩展。或者我们去继承一个接口或抽象类来达到扩展的目的。总之,核心就是抽象

为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。在像Java、C++这种面相对象的编程语言中,通常是给系统定义一个相对稳定的抽象层,将不同的实现行为移到具体的实现层中完成。当需要改动系统行为的时候,不需要对抽象层进行修改,只需要增加新的具体类来实现新的业务功能即可。从而实现在不修改已有代码的基础上扩展系统的功能,达到开闭原则的要求

假设我按照如下的逻辑来实现王者荣耀的射手属性的英雄


<?php

class Shooter

{

public function firstSkill($hero)

{

if ($hero == 'baiLi') {

$obj = new Baili();

$obj->firstSkill();

} elseif ($hero == 'mengYa') {

$obj = new Mengya();

$obj->firstSkill();

}

}

public function SecondSkill($hero)

{

if ($hero == 'baiLi') {

$obj = new Baili();

$obj->SecondSkill();

} elseif ($hero == 'mengYa') {

$obj = new Mengya();

$obj->SecondSkill();

}

}

public function ThirdSkill($hero)

{

...

}

}

class Baili

{

public function firstSkill()

{

echo "百里的一技能";

}

public function SecondSkill($hero)

{

echo "百里的二技能";

}

public function ThirdSkill($hero)

{

echo "百里的三技能";

}

}

class Mengya

{

public function firstSkill()

{

echo "蒙犽的一技能";

}

public function SecondSkill($hero)

{

echo "蒙犽的二技能";

}

public function ThirdSkill($hero)

{

echo "蒙犽的三技能";

}

}

上边这种方式,如果我现在要增加一个英雄,就需要修改Shooter(射手类)类的三个方法,增加新的判断逻辑,这就违反了开放封闭原则

现在将射手类抽象出来


<?php

abstract class Shooter

{

abstract function firstSkill();

abstract function SecondSkill();

abstract function ThirdSkill();

}

class OperateShooter

{

private $shooter;

public function setShooter(Shooter $shooter)

{

$this->shooter = $shooter;

}

public function firstSkill()

{

$this->shooter->firstSkill();

}

}

class Baili extends Shooter

{

public function firstSkill()

{

echo "百里的一技能";

}

public function SecondSkill()

{

echo "百里的二技能";

}

public function ThirdSkill()

{

echo "百里的三技能";

}

}

class Mengya extends Shooter

{

public function firstSkill()

{

echo "蒙犽的一技能";

}

public function SecondSkill()

{

echo "蒙犽的二技能";

}

public function ThirdSkill()

{

echo "蒙犽的三技能";

}

}

$obj = new OperateShooter();

$obj->setShooter(new Baili());

$obj->firstSkill();

输出:

百里的一技能

重构之后,封装了一个抽象类(Shooter),并且OperateShooter针对射手这个抽象类进行了操作,通过setShooter()方法,可以让使用者来设置实例化具体的射手英雄对象,通过OperateShooter的firstSkill()方法来调用传过来的对象的firstSkill()方法。假设此时增加了一个新的射手英雄,只需要将新的射手英雄继承射手抽象类(Shooter),在使用这个新英雄的一技能时,直接向OperateShooter类中注入一个新英雄的对象即可,这样就不用次改现有类的代码

image.png

阅读 715
34 声望
20 粉丝
0 条评论
你知道吗?

34 声望
20 粉丝
宣传栏