前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式之 六大原则

设计模式之 六大原则

作者头像
用户3094376
发布2018-09-12 11:05:05
4850
发布2018-09-12 11:05:05
举报
文章被收录于专栏:gaoqin31gaoqin31gaoqin31

一.单一职责

定义: 一个类承担的职责不宜过多,或者说就一个类而言,应该仅有一个引起它变化的原因

如果一个类的职责承担过多,如果涉及到其中每一个职责变动的时候,都要修改这个类,而且在我们要复用这个类中的其中一个职责的时候也没法做到复用。

class Act{
    
    public function run(){
        $data = $this->curl($url);
    }
    
    public function curl($url, $data = array(), $timeout = 5) {
        $ch = curl_init();
        if (!empty($data) && $data) {
            if(is_array($data)){
                $formdata = http_build_query($data);
            } else {
                $formdata = $data;
            }
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $formdata);
        }
        
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_ENCODING, '');
        $result = curl_exec($ch);
        curl_close($ch);
        return $result;
    }
}

看到项目里经常有把请求第三方接口的curl方法写在当前类里,比如上面的活动类Act耦合了curl方法,如果此时又有另外一个类要使用curl方法,这个类并不需要活动类的其它功能此时就不能复用Act的curl方法 只能在这个类里在增加一个curl方法,如此下去,随着类越来越多,项目里到处是curl方法,假如某一天需要把超时改成10秒,那就惨了,得一个个类里去修改。其实我们完全可以把curl方法放到curl类里,将Act类和curl解耦, 这样项目统一使用curl类的curl方法,新增功能的时候可复用Curl的curl方法,修改的时候只需修改Curl类。

class Act{
    
    public function run(){
        $data = Curl::curl($url);
    }
}

class Curl{
    public static function curl($url, $data = array(), $timeout = 5) {
        /***代码略***/
    }
}

二.开放封闭原则

定义: 软件实体(类,函数模块) 应该是可以扩展的,但是不可以修改

这应该是软件开发的理想境界,其实我们不可能做到完全对修改关闭,只是尽量做到即可。 比如游戏里的支付回调,可以抽象为3个步骤,验证第三方参数,发道具给玩家,返回报文给第三方。

abstract class Notify{
    //回调状态码
    protected $_status = 0;
    protected abstract function checkSign();
    protected abstract function SendProp();
    protected abstract function OutputMessage();
    
    public final function run(){
        if($this->checkSign()){
            if($this->SendProp()){
                //成功写入数据记录
                $this->_writeDBLog();
                $this->_status = 1;
            }else{
                $this->_status = -1;//发道具错误
            }
        }else{
            $this->_status = 2;//第三方参数错误
        }
        $this->OutputMessage();
    }
    private function _writeDBLog(){
        echo "写入DB日志\n";
    }
}

class Weixin extends Notify{
    protected function checkSign(){
        return true;
    }
    protected function SendProp(){
        return true;
    }
    protected function OutputMessage(){
        if($this->_status == 1){
            die('ok');
        }else{
            die('fail');
        }
    }
}

$weixin = new Weixin();
$weixin ->run();

上面是一个第三方回调的主体代码,我抽掉了其他的业务逻辑,每个支付子类继承子一个抽象父类,这样做的好处是可以把公共的逻辑放到父类里,无需每个子类重复写公共逻辑,假如要新增支付宝, 只需要新增一个支付宝类,然后实现子类特有的业务逻辑即可。当然如果某天要新增文件日志,那必须改父类了,团队里开发人员水平参差不齐,我们可以把 Notify父类这个文件设置为指定人员可改。 这样基本做到了对扩展开防,对修改关闭。

三.里氏替换原则

定义: 子类型必须能够替换掉他们的父类型

意思是尽量不要重写父类的方法,因为父类的方法一般是公用的

由于世界上最好的语言是弱类型,这个原则PHP实在无法理解,这里使用java来理解这个原则

class Car{
    String carType;
    public Car(String carType){
        this.carType = carType;
    }   
    public void run(){
        System.out.println(carType + "跑");
    }   
}

class HondaCar extends Car{
    public HondaCar(String carType){
        super(carType);
    }   
    
    /*  
    public void run(){
        throw new Exception();
    }
    */
}

class FordCar extends Car{
    public FordCar(String carType){
        super(carType);
    }   
}

public class Client {
    public static void main(String[]agrs){
        Car h = new HondaCar("丰田");   
        h.run();

        FordCar f = new FordCar("福特");
        f.run();    
    }   
}

子类型FordCar f对象,可以替换掉父类型Car h对象,但假如 HondaCar类重写父类的run方法,显然替换会造成程序异常。 然而有的时候我们不得不重写父类的方法,只要重写父类方法能给我们带来大利大于弊的时候即可。

四.依赖倒转原则 

定义 : 抽象不应该依赖于细节,细节应该依赖于抽象,要针对接口编程而不是对具体实现编程

这个原则还是用java来理解

class Members{
    public void set(){
        Redis redis = new Redis();
        redis.set("u_key", "u_value");
    }   
}

class Redis{
    public void set(String key, String val){
        System.out.println("Redis设置key:" + key + "value :" + val + "成功!");
    }   
}

public class Client {
    public static void main(String[]agrs){
        Members m = new Members();
        m.set();
    }   
}

假如存贮换为Mysql那么我们得修改Members类,我们改下代码,增加一个Istore接口,Redis和Mysql类实现set方法。具体的类名我们可以写到配置文件里,然后通过反射实例化相应的对象, 换存贮只需要修改配置文件,增加对应的存储类即可做到符合开闭原则。

interface Istore{
    public void set(String key, String val);
}

class Members{
    public void set(){
        Istore store  = new Mysql();
        store.set("u_key", "u_value");
    }   
}

class Mysql implements Istore{
    public void set(String key, String val){
        System.out.println("Mysql设置key:" + key + "value :" + val + "成功!");
    }   
}

class Redis implements Istore{
    public void set(String key, String val){
        System.out.println("Redis设置key:" + key + "value :" + val + "成功!");
    }   
}

public class Client {
    public static void main(String[]agrs){
        Members m = new Members();
        m.set();
    }   
}

五.迪米特法则 

定义 : 一个类应该尽量降低自己成员的访问权限,如果两个类不必发生直接通讯,那么这两个类就不应该直接发生相互作用,如果其中一个类要调用另外一个类的方法,可以通过第三者转发这个调用

这个原则强调尽量降低类和类之间的耦合度,一个处于松耦合的类一旦被修改,不会对关联的类造成太大的影响

class Cache{
    function getResult(){
        return array('id'=>1, 'name'=>'PHP');
    }
}

class Mysql{
    function getResult(){
        return array('id'=>1, 'name'=>'PHP');
    }
}

class Client{
    public static function main(){
        $data = array();
        $cache = new Cache();
        if(!$data = $cache->getResult()){
            $db = new Mysql();
            $data = $db->getResult();
        }
        print_r($data);
    }
}
Client::main();

上面这个是我们经常碰到的业务场景,先从cache里取数据,取不到就从db里取,万一要加个文件里取,就得修改客户端配置,如果调用的地方很多,每个地方都得改。 此时根据迪米特法则完全可以引入一个对象当中间人,使得客户端不用直接和db,cache交互。

class Cache{
    function getResult(){
        return array('id'=>1, 'name'=>'PHP');
    }
}

class Mysql{
    function getResult(){
        return array('id'=>1, 'name'=>'PHP');
    }
}

class Proxy{
    static function getResult(){
        $data = array();
        $cache = new Cache();
        if(!$data = $cache->getResult()){
            $db = new Mysql();
            $data = $db->getResult();
        }
        return $data;
    }
}

class Client{
    public static function main(){
        $data = Proxy::getResult();
        print_r($data);
    }
}
Client::main();

六.接口隔离原则 

定义 : 不要建立庞大臃肿的接口,接口中的方法尽量少

 如果违反这一原则,有时候不需要这些方法子类也不得不实现这个方法。

 下面以高考作为一个栗子(纯属虚构), 最初教育部规定各省市考试有7科,部分文理,这样造成喜欢文科的也不得不考理科,喜欢理科的不得不考文科,上海表示抗议,要分文理科,于是上海私自做决定, 考试我还是考(实现全国卷统一接口),但是文科生理综不计入总分(空方法实现),理科生分科不计入总分(空方法实现), 接着北京,天津等直辖市纷纷效仿,深圳广州等城市还是按照综合考试,觉得这样有利于学生综合能力发展,这时候教育部想了个办法把学科分成主课+文+理(三个接口,这样做到了接口最小化), 愿意分文理就靠文科的,愿意综合的就靠所有的,这样大家都没意见了。

如果某些时候遵守规则让你开发效率更低,那还不如违反规则以得到更好的开发效率,规则是死的,人是活的,我们开发的时候尽量遵守这些规则即可

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-09-28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档