不一样的面向对象(二)

设计模式六大原则

单一职责原则(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

119 声望
32 粉丝
0 条评论
推荐阅读
Go编译原理系列10(逃逸分析)
在上一篇文章中分享了编译器的优化方法之一:函数内联,本文分享编译器的另一个优化方法:逃逸分析。逃逸分析是Go语言编译过程中比较重要的一个优化阶段,它主要用于标识变量应该被分配到栈上还是堆上

书旅阅读 480

怎样用 PHP 来实现枚举?
在数学和计算机科学理论中,一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。这两种类型经常(但不总是)重叠。枚举是一个被命名的整型常数的集合,枚举在日常生活中很常见,...

唯一丶25阅读 6.3k评论 4

图片防盗链破解 解决图片防盗链问题 反向代理
当客户端(浏览器)向服务器请求内容的时候,会提交一个header,这个header中包含了如:浏览器信息、cookie等内容,那么有一个叫referer的东东,也包含在这里面。

TANKING7阅读 11.2k评论 5

Git操作不规范,战友提刀来相见!
年终奖都没了,还要扣我绩效,门都没有,哈哈。这波骚Git操作我也是第一次用,担心闪了腰,所以不仅做了备份,也做了笔记,分享给大家。问题描述小A和我在同时开发一个功能模块,他在优化之前的代码逻辑,我在开...

王中阳Go5阅读 2k评论 2

封面图
PHP转Go实践:xjson解析神器「开源工具集」
我和劲仔都是PHP转Go,身边越来越多做PHP的朋友也逐渐在用Go进行重构,重构过程中,会发现php的json解析操作(系列化与反序列化)是真的香,弱类型语言的各种隐式类型转换,很大程度的减低了程序的复杂度。

王中阳Go6阅读 1.3k评论 2

封面图
微信公众号开发:自动回复文本/图片/图文消息/关键词回复/上传素材/自定义菜单
对接流程1、申请微信公众号测试账号URL:[链接]2、登录,配置开发者服务器URL和Token开发者服务器配置代码:config.php {代码...} URL是config.php在你服务器的URLToken是上面代码自己设置的Token搞定之后,就能完...

TANKING2阅读 10k

Ajax实现搜索联想 搜索关键词提醒 无刷新搜索
通过javascript监听搜索框的内容,调用后端即可。(1)javascript监听搜索框的内容(2)把搜索框的关键词传给后端进行搜索(3)搜索到结果,遍历到页面

TANKING1阅读 4.3k

119 声望
32 粉丝
宣传栏