设计原则之开放闭合原则(OCP)

在《敏捷软件开发-原则、模式与实践》一书中,对开放闭合原则的定义为:

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

简言之,两个特征:

1、对于扩展是开放的;

2、对于修改是关闭的

这两点特征貌似自相矛盾,怎么样在不改动模块源码的情况下去更改他的行为呢?如果不更改一个模块,又怎么能够去改变它的功能呢?

答案很简单,就是抽象。模块可能对抽象体进行操作。由于模块依赖于一个固定的抽象体,所以他对于更改可以是封闭的。同时,通过从这个抽象体去派生,可以扩展此模块的行为。


设计原则开篇一文中已经举例说明了如何应用这个原则。在这篇文章中再举一个例子,帮助大家加深认识。


如果你有去看zend framework的源码,其中有一段代码是用来解析配置文件的,这次就拿这个案例来举例。

version1:

有多个配置文件:config.php、config.json、config.xml

config.php:

<?php

return array(
	'database' => array(
			'host' => '127.0.0.1',
			'username' => 'root',
			'password' => '',
			'dbname' => 'test'
		)
);
config.json:
{
	"database":{
		"host":"127.0.0.1",
		"username":"root",
		"password":"",
		"dbname":"test"
	}
}
config.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<config>
	<database>
		<host>127.0.0.1</host>
		<username>root</username>
		<password></password>
		<dbname>test</dbname>
	</database>
</config> 

我们将配置文件交给config类去处理:

public function configToArray($configFile){
     	$extension = pathinfo($configFile, PATHINFO_EXTENSION);
     	$config = array();
		switch ($extension) {
		 	case 'php':
		 		$config = require_once $configFile;
		 		break;

		 	case 'xml':
                $xml = simplexml_load_file($configFile);
                return json_decode(json_encode($xml), true);

		 	case 'json':
		 		$config = json_decode(file_get_contents($configFile), true);
		 		break;
		 }
		 foreach ($config as $key => $value) {
		 	$this->set($key, $value);
		 }
     }


这样的弊端在于,后期如果希望使用新的配置文件,比如ini文件、yaml文件等,我们就需要到switch语句里面去新增case。

现在来看version 2的设计:

首先声明了一个接口Configuration:

<?php

interface Configuration{
    public function toArray($configFilePath);
}
然后新增了3个具体的实现类:

jsonConfiguration.php

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

<?php
require_once "configuration.php";
class phpConfiguration implements Configuration{
    public function toArray($configFilePath){
        $config = require_once $configFilePath;
        return $config;
    }
}
xmlConfiguration.php
<?php
require_once "configuration.php";
class XmlConfiguration implements Configuration{
    public function toArray($configFilePath){
        $xml = simplexml_load_file($configFilePath);
        return json_decode(json_encode($xml), true);
    }
}
这样config类中的代码就可以修改为:

public function configToArray(Configuration $configuration){
     	$config = is_array($configuration->toArray($this->configFilePath)) ? $configuration->toArray($this->configFilePath) : array();
		 foreach ($config as $key => $value) {
		 	$this->set($key, $value);
		 }
     }

这样我们想再增加一个配置文件时,就可以新增一个实现类就可以了。也就做到了“对修改关闭,对扩展开放”了。

这个案例和设计原则开篇一文中提到的案例非常相似,可以概括为“如何将if/else转换为抽象”和“如何将switch转换为抽象”,偷笑


在许多方面,OCP都是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所声称的巨大好处:灵活性、可重用性和可维护性。然而,并不是说只要使用一种面向对象语言就是遵循了这个原则。对于应用程序中的每个部分都肆意地进行抽象同样不是一个好主意。正确的做法是,开发人员应该仅仅对程序中呈现出频繁变化的那些部分做出抽象。拒绝不成熟的抽象和抽象本身一样重要。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值