开-闭原则:
一个软件实体应该对扩展开放,对修改关闭。
理解: 在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展。
其实这里所说的不被修改是指的重要的抽象层的模块不会被修改,这也就是使变化中的软 系统有一定的稳定性。当系统要扩展或者添加新的行为的时候只需要添加 另外实现的模块即可。由于新添加的模块继承于抽象层,所以实现了其不变性。
比如在策略模式中,OCP就得到了很好的体现,算法的不同实现其实就是对扩展的支持,而算法抽象类是对系统的不变性的支持,环境类包装了对于环境变化的控制与所采用算法的选择,当采用其他算法的时候只需要扩展算法类即可。
也就是说关键在于抽象,抽象出来的东西是不变的,具体的实 现继承于抽象,所以保证了对修改的Close,而抽象的实现方式有多种,可以随需添加,当然这也就是对扩展的Open。 另外要求的是技术包括:多态 Polymorphism,接口 Interface,继承 Inheritance.
满足开闭原则的模块符合下面两个标准:
- 对扩展开放 ------- 模块的行为可以被扩展从而满足新的需求。
- 对修改关闭 ------- 不允许修改模块的源代码。(或者尽量使修改最小化)
这两个标准看似相互矛盾的,那么我们怎么实现他们呢?
怎样实现开闭原则?
- 抽象
- 多态
- 继承
- 接口
为什么要遵循开放关闭原则?
一个设计良好的应用程序应该充分考虑到开发和维护阶段需求的频繁变化,通常情况下,添加一个新的功能需要做出很多修改,我们应该使对已有代码的修改最小化,因为他们已经经过了测试。对现有代码做出修改将会以一种不可预料的方式影响它们的已有功能。
Example:
考虑下面某个类的方法:
- public double totalPrice(Part[] parts) {
- double total = 0.0;
- for (int i=0; i<parts.length; i++) {
- total += parts[i].getPrice();
- }
- return total;
- }
上面函数的功能是计算给定的零件数组中所有零件价格的总和,如果Part是一个基类或者接口,那我们就可以利用多态的特性,当有新的零件被添加进来时不需要修改该函数的代码。这样它就可以满足开闭原则。
但是如果我们的会计部门规定当计算主板和内存的价格时,需要添加一些额外的费用,请看下面的代码:
- public double totalPrice(Part[] parts) {
- double total = 0.0;
- for (int i=0; i<parts.length; i++) {
- if (parts[i] instanceof Motherboard)
- total += (1.45 * parts[i].getPrice());
- else if (parts[i] instanceof Memory)
- total += (1.27 * parts[i].getPrice());
- else
- total += parts[i].getPrice();
- }
- return total;
- }
现在它还符合开闭原则吗?不!每次会计部门发布一个新的价格政策时,我们都需要修改totalPrice()方法!它对修改不是关闭的,显然,价格政策的改变意味着我们必须修改某处的代码,那么我们应该怎么做呢?为了使用我们第一个版本的totalPrice()方法,我们需要把Part的getPrice()方法的价格政策包含进来。
下面是Part和ConcretePrat类:
- // Class Part is the superclass for all parts.
- public class Part {
- private double price;
- public Part(double price) (this.price = price;}
- public void setPrice(double price) {this.price = price;}
- public double getPrice() {return price;}
- }
- // Class ConcretePart implements a part for sale.
- // Pricing policy explicit here!
- public class ConcretePart extends Part {
- public double getPrice() {
- // return (1.45 * price); //Premium
- return (0.90 * price); //Labor Day Sale
- }
- }
但是,现在如果价格政策改变,我们必须修改Part的子类,一个更好的方法是建立一个PricePolicy类,它可以为我们提供不同的价格政策:
- /**
- * Class PricePolicy implements a given price policy.
- */
- public class PricePolicy {
- private double factor;
- public PricePolicy (double factor) {
- this.factor = factor;
- }
- public double getPrice(double price) {return price * factor;}
- }
使用这种方法,我们可以在运行时动态的设置Part对象所引用的PricePoilcy对象,在实际的程序中,零件的价格和相关的PricePolicy可以从数据库中获取。
其实这里所说的不被修改是指的重要的抽象层的模块不会被修改,这也就是使变化中的软 系统有一定的稳定性。当系统要扩展或者添加新的行为的时候只需要添加 另外实现的模块即可。由于新添加的模块继承于抽象层,所以实现了其不变性。
比如在策略模式中,OCP就得到了很好的体现,算法的不同实现其实就是对扩展的支持,而算法抽象类是对系统的不变性的支持,环境类包装了对于环境变化的控制与所采用算法的选择,当采用其他算法的时候只需要扩展算法类即可。
也就是说关键在于抽象,抽象出来的东西是不变的,具体的实 现继承于抽象,所以保证了对修改的Close,而抽象的实现方式有多种,可以随需添加,当然这也就是对扩展的Open。 另外要求的是技术包括:多态 Polymorphism,接口 Interface,继承 Inheritance.
注:另外在对可变性进行封装的时候也应该注意以下几点:
1.识别系统有可能变化的地方。
2.不要将一种可变形散布在多处代码,而应该封装起来。
3.不要将一种可变性与另外一种可变性混在一起。
1.识别系统有可能变化的地方。
2.不要将一种可变形散布在多处代码,而应该封装起来。
3.不要将一种可变性与另外一种可变性混在一起。
去观察及思考:变的与不变的块!