面向对象编程之开放闭合原理的

Posted 步步为营WEB

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象编程之开放闭合原理的相关的知识,希望对你有一定的参考价值。

  开放封闭原则(OCP,Open Closed Principle)是所有面向对象原则的核心。软件设计本身所追求的目标就是封装变化、降低耦合,而开放封闭原则正是对这一目标的最直接体现。

  关于开放封闭原则,其核心的思想是:
  软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的
  因此,开放封闭原则主要体现在两个方面:
  对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
  对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。
  “需求总是变化”、“世界上没有一个软件是不变的”,这些言论是对软件需求最经典的表白。从中透射出一个关键的意思就是,对于软件设计者来说,必须在不需要对原有的系统进行修改的情况下,实现灵活的系统扩展。而如何能做到这一点呢?
  只有依赖于抽象实现开放封闭的核心思想就是对抽象编程,而不对具体编程,因为抽象相对稳定。让类依赖于固定的抽象,所以对修改就是封闭的;而通过面向对象的继承和对多态机制,可以实现对抽象体的继承,通过覆写其方法来改变固有行为,实现新的扩展方法,所以对于扩展就是开放的。
  
  举个栗子:
  
 
  1.银行业务办理
   A.没有实现开放闭合原理的方式:
    
public class BankProcess
    {
        public void Deposite(){}   //存款
        public void Withdraw(){}   //取款
        public void Transfer(){}   //转账
    }

    public class BankStaff
    {
        private BankProcess bankpro = new BankProcess();
        public void BankHandle(Client client)
        {
            switch (client .Type)
            {
                case "deposite":      //存款
                    bankpro.Deposite();
                    break;
                case "withdraw":      //取款
                    bankpro.Withdraw();
                    break;
                case "transfer":      //转账
                    bankpro.Transfer();
                    break;
            }
        }
    }
View Code

    目前设计中就只有存款,取款和转账三个功能,将来如果业务增加了,比如增加申购基金功能,理财功能等,就必须要修改BankProcess业务类。我们分析上述设计就能发现不能把业务封装在一个类里面,违反单一职责原则,而有新的需求发生,必须修改现有代码则违反了开放封闭原则。

    那么,如何使代码耦合度更低?而不是牵一发儿动全身,前辈们已经给我们趟出了一些路子:将业务功能抽象为接口,当业务员依赖于固定的抽象时,对修改就是封闭的,而通过继承和多态继承,从抽象体中扩展出新的实现,就是对扩展的开放。

   B.实现了开放闭合原理的方式:

    public interface IBankProcess //首先声明一个业务处理接口
    {
        void Process();
    }
    public class DeposiProcess:IBankProcess
    {
        public void Process()         //办理存款业务
        {
            Console.WriteLine("Process Deposit");
        }
    }
    public class WithDrawProcess:IBankProcess
    {
        public void Process()        //办理取款业务
        {
            Console.WriteLine("Process WithDraw");
        }
    }
    public class TransferProcess:IBankProcess
    {
        public void Process()        //办理转账业务
        {
            Console .WriteLine ("Process Transfer");
        }
    }
    public class BankStaff
    {
        private IBankProcess  bankpro = null ;
        public void BankHandle(Client client)
        {
            switch (client .Type)
            {
                case "Deposite":      //存款
                    userProc =new WithDrawUser();
                    break;
                case "WithDraw":      //取款
                    userProc =new WithDrawUser();
                    break;
                case "Transfer":      //转账
                    userProc =new WithDrawUser();
                    break;
            }
            userProc.Process();
        }
    }
View Code

  银行工作人员:

class BankStaff
{
private IBankProcess bankProc = null;
public void HandleProcess(Client client)
{
bankProc = client.CreateProcess();
bankProc.Process();
}
}
View Code

  客户:

class Client
{
private string ClientType;
public Client(string clientType)
{
ClientType = clientType;
}
public IBankProcess CreateProcess()
{
switch (ClientType)
{
case "存款用户":
return new DepositProcess();
break;
case "转账用户":
return new TransferProcess();
break;
case "取款用户":
return new DrawMoneyProcess();
break;
}
return null;
}
}
View Code

  我们办理业务的时候:

class BankProcess
{
public static void Main()
{
EasyBankStaff bankStaff = new BankStaff();
bankStaff.HandleProcess(new Client("转账用户"));
}
}
View Code

  当有新的业务增加时,银行经理不必为重新组织业务流程而担忧,你只需为新增的业务实现IBankProcess接口:

class FundProcess : IBankProcess
{
//IBankProcess Members
#region IBankProcess Members
public void Process()
{
// 办理基金业务
throw new Exception("The method or operation is not implemented.");
}
#endregion
}
View Code

  新的设计遵守了开放封闭原则,在需求增加时只需要向系统中加入新的功能实现类,而原有的一切保持封闭不变的状态,这就是基于抽象机制而实现的开放封闭式设计。

  2.系统配置文件读取

  配置文件有多重文件格式:php,ini,json,xml等

  我们的原则:封装变化,对扩展开放,对修改闭合

  首先,增加抽象接口:

<?php  
interface Configuration{  
    public function toArray($configFilePath);  
}  
?>
View Code

  然后,具体实现类继承接口:

  phpConfiguration.php

<?php  
require_once "configuration.php";  
class phpConfiguration implements Configuration{  
    public function toArray($configFilePath){  
        $config = require_once $configFilePath;  
        return $config;  
    }  
}  
?>
View Code

  jsonConfiguration.php

<?php  
require_once "configuration.php";  
class JsonConfiguration implements Configuration{  
    public function toArray($configFilePath){  
        return json_decode(file_get_contents($configFilePath), true);  
    }  
}  
?>
View Code

  给出config.php配置工具类:

<?php
require_once "phpConfiguration.php";
class config{
    var $configFilePath;
    //定义一个构造方法初始化赋值
    function __construct($configFilePath) {
        $this->configFilePath=$configFilePath;
    }
    public function configToArray($configuration){
        $result =$configuration->toArray($this->configFilePath);
        $config = is_array($result) ? $result : array();
        return $config;
    }

}
?>
View Code

完整例子下载:配置文件开放闭合原则实例

 

  3.媒体播放器实例

  以电脑中的多媒体播放软件为例,作为一款播放器,应该具有一些基本的、通用的功能,如打开多媒体文件、快进、音量调剂等功能。不论在什么平台下,遵循这个原则设计的播放器都应该有统一的操作规划和操作习惯,都应该保证操作者能够很快上手。

  首先,定义一个抽象业务接口:

<?php
interface process  
{  
      public function process();  
}  
?>
View Code

  然后,对此接口进行拓展,实现解码和输出的功能:

<?php
class playerencode implements process   
{  
      public function process()  
      {  
            echo "encode\\r\\n";  
      }  
}  
  
class playeroutput implements process   
{  
      public function process()  
      {  
            echo "output";  
      }  
}  
?>
View Code

  接下来定义播放器的线程调度处理器:

class playProcess  
{  
      private $message = null;  
      public function __construct()  
      {  
  
      }  
      public function callback(event $event) {  
            $this->message = $event->click();  
            if($this->message instanceof process) {  
                  $this->message->process();  
            }  
      }  
}  
View Code

  然后,在定义一个mp4类,这个类相对是封闭的,其中定义时间的处理逻辑。

class Mp4  
{  
      public function work() {  
            $playProcess = new playProcess();  
            $playProcess->callback(new event(\'encode\'));  
            $playProcess->callback(new event(\'output\'));  
      }  
}  
View Code

  最后,增加一个事件分拣的处理类,此类负责对事件进行分拣,判断用户或内部行为,供播放器的线程调度器调度。

class event  
{  
      private $m;  
      public function __construct($me)  
      {  
            $this->m = $me;  
      }  
        
      public function click() {  
            switch ($this->m) {  
                  case \'encode\':  
                        return new playerencode();  
                  break;  
                  case \'output\':  
                        return new playeroutput();  
                  break;  
            }  
      }  
}  
View Code

完整例子下载:播放器开放闭合原则实例

 

三个栗子,应该大概能理解开放闭合原理了。

 

总结一下

实现开发-封闭原则的思想就是对抽象编程,而不是具体编程,因为抽象相对稳定,让类依赖于固定的抽象,这样的修改时封闭的;而通过对象的继承和多态机制,可以实现对抽象类的继承,通过覆盖其方法来修改固有行为,实现新的拓展方法,所以对于拓展就是开放的。

(1)在设计方面充分利用“抽象”和“封装”的思想

一方面也就是在软件系统中找到各种可能的“可变因素”,并将之封装起来

另一方面,一种可变因素不应当散落在不同代码模块中,而应当被封装到一个对象中。

(2)在系统功能编程实现方面利用面向接口的编程

当需求发生变化时,可以提供接口新的实现类,以求适应变化

面向接口编程要求功能类实现接口,对象声明为借口类型。

 

参考文献:

https://yq.aliyun.com/articles/45638  设计模式六大原则——开放封闭原则(OCP)

http://blog.csdn.net/u011250882/article/details/47358519  设计原则之开放闭合原则(OCP)

http://blog.csdn.net/dnidong/article/details/57401935  php面向对象的设计原则之开发-封闭原则(OCP)

以上是关于面向对象编程之开放闭合原理的的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——JS中的面向对象编程

VSCode自定义代码片段9——JS中的面向对象编程

面向面试编程代码片段之GC

Python之装饰器简介

直击JavaScript之面向对象

直击JavaScript之面向对象