写点什么

不一样的面向对象(二)

用户头像
书旅
关注
发布于: 2020 年 09 月 25 日
不一样的面向对象(二)

设计模式六大原则

单一职责原则(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类中注入一个新英雄的对象即可,这样就不用次改现有类的代码





发布于: 2020 年 09 月 25 日阅读数: 81
用户头像

书旅

关注

公众号:IT猿圈 2019.04.11 加入

还未添加个人简介

评论

发布
暂无评论
不一样的面向对象(二)