• 设计模式之 六大原则


    一.单一职责

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

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

    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日志
    ";
        }
    }
    
    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科,部分文理,这样造成喜欢文科的也不得不考理科,喜欢理科的不得不考文科,上海表示抗议,要分文理科,于是上海私自做决定, 考试我还是考(实现全国卷统一接口),但是文科生理综不计入总分(空方法实现),理科生分科不计入总分(空方法实现), 接着北京,天津等直辖市纷纷效仿,深圳广州等城市还是按照综合考试,觉得这样有利于学生综合能力发展,这时候教育部想了个办法把学科分成主课+文+理(三个接口,这样做到了接口最小化), 愿意分文理就靠文科的,愿意综合的就靠所有的,这样大家都没意见了。

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

     

  • 相关阅读:
    实现翻页效果
    使用AHKActionSheet
    [翻译] GSProgressView
    [翻译] NSRegexTester
    下载时获取文件大小
    高级mask应用
    使用UILabel实现滚动字幕移动效果
    动画绘制水波纹
    能产生粒子效果的CAEmitterLayer
    使用UITableView实现图片视差效果
  • 原文地址:https://www.cnblogs.com/gaoqin31/p/7608243.html
Copyright © 2020-2023  润新知