• 面向对象设计原则之开闭原则


    两截门--一个被水平分割为两部分的门,这样每一部分都可以独立保持开放或封闭

    开放-封闭原则(The Open-Closed Principle)

    软件实体(类、模块、函数)应该是可以扩展的,但是不可以修改的。
    如果程序中的一处改动就会产生连锁反应,导致一系列的相关模块的改动,那么设计就具有僵化的臭味。如果正确的应用OCP,那么以后再进行同样的改动时,就只需要添加新的代码,而不必改动已经正常运行的代码。

    描述

    主要两个特征:

    1. “对于扩展是开放的”
      模块的行为是可以扩展的,当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。简言之,我们可以改变模块的功能。
    2. “对于更改是封闭的”
      对模块进行扩展时,不必改动模块的源代码。

    关键是抽象

    下图展示了一个简单的不遵循OCP的设计。Client类和Server类都是具体类,Client类使用Server类。如果我们希望Client对象使用另外一个不同的服务器对象,那么就必须要把Client类中使用Server类的地方更改为新的服务器类

    下图是根据OCP设计重构的设计

    ClientInterface类是一个拥有抽象成员函数的抽象类,Client类使用这个抽象类。如果我们希望Client对象使用一个不同的服务器类,那么只需要冲ClientInterface类派生一个新的类,无需对Client类做任何改动。

    Shape应用程序

    应用程序中有Circle、Square列表,需求是:绘制他们
    从OCP原则考虑,设计方案如下,

    class Shape{
    draw();
    }
    
    class Circle extends Shape{
        draw(){
        
        }
    }
    
    class Square extends Shape{
        draw(){
    
        }
    }
    
    Class DrawAllShape{
        void drawAllShapes(List<Shape> lists){
            for(Shape shape :lists){
                shape.draw();
            }
        }
    }
    

    根据此设计,如果新增一个Triangle类,只需新增代码即可,无需改动上述代码,达到了开闭原则。真的吗???

    并非100%封闭

    如果需求要求所有圆必须在正方形之前绘制,那么DrawAllShape无法对这种变化做到封闭。要实现这个需求,我们需要修改DrawAllShape,使它首先扫描列表中的圆,然后在扫描所有的正方形。
    

    预测变化和贴切的结构

    一般来说,无论模块是多么封闭,都会存在一些无法对之封闭的变化,没有对于所有的情况都贴切的模型。
    既然不能完全封闭,就要有策略的对待这个问题,设计人员必须对于他设计的模块应该对哪种变化封闭作出选择,他必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化。这需要设计人员具备一些从经验中获得的预测能力。
    遵循OCP的代价是昂贵的,创建正确的抽象是要花费开发时间和精力的,同时,那些抽象也增加了软件设计的复杂性,开发人员有能力处理的抽象的数量也是有限的,显然,我们希望把OCP的应用限定在可能会发生的变化上。
    
    • 只受一次愚弄
      最初编写代码的时候,假设变化不会发生,当变化发生了,我们就创建抽象来隔离以后发生的同类变化。
    • 刺激变化
      尽早查明可能发生的变化,尽早接受变化带来的改变。

    Shape改造

    class Shape{
        draw();
        //precedes();
        precedes(OrderRule order){
        }
    }
    Class DrawAllShape{
        sort(list);
        void drawAllShapes(List<Shape> lists){
            for(Shape shape :lists){
                shape.draw();
            }
        }
    }
    

    precedes()考虑到枚举排序的话违背了OCP,可以将规则定义起来,改造成precedes(OrderRule order),排序规则从OrderRule中读取,此时对与Shape绘制顺序的变化不封闭的唯一部分就是OrderRule。

    结论

    在许多方面,OCP都是面向对象设计的核心所在。遵循这个原则可以的带来面向对象技术所声称的巨大好处(灵活性,可重用性,可维护性)。对于应用程序中的每个部分都肆意的进行抽象同样不是一个好主意。正确的做法是,开发人员应该仅仅对程序中呈现出频繁变化的那些部分做出抽象。拒绝不成熟的抽象和抽象本身一样重要。
  • 相关阅读:
    HDU 5714
    C++ 中的名称冲突之 "y1"
    FFT 模板
    Modular Query
    找礼物(find)(模拟)
    水流(water)(BFS)(DFS)
    单词接龙(dragon)(BFS)
    细菌(disease) (位运算)(状态压缩)
    Diamond Collector (动态规划)
    超级素数(sprime) (BFS)
  • 原文地址:https://www.cnblogs.com/vincent0928/p/6568354.html
Copyright © 2020-2023  润新知