开放-封闭原则(OCP)

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

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

两个特征

1、对于扩展是开放的(Open for extension);

2、对于更改是封闭的(Close for modification)。

关键是抽象

既不开放又不封闭的Client

        Client类和Server类都是具体类。Client类使用Server类。如果我们希望Client对象使用另外一个不同的服务器对象,那么就必须把Client类中使用Server类的地方更改为新的服务器类。

Strategy 模式:既开放又封闭的Client

        Client需要实现一些功能,可以使用ClientInterface接口去描述那些功能。子类可以选择它们的方式去实现这个接口。这样,就可以通过创建ClientInteface的新的子类的方式去扩展、更改Client的行为。

        Why?把接口命名为ClientInterface,而非AbstractServer。因为抽象类和Client的关系更紧密。

Shape 应用程序

1、违反OCP

        drawAllShapes()不符合OCP,因为它对于新的形状类型的添加不是封闭的。每添加一种新的形状类型,都必须要更改这个函数。

public enum ShapeTpye {

    CIRCLE, SQUARE
}

public class Shape {
    ShapeType itsType;
}

public class Circle extends Shape {
    ShapeType itsType;
    double itsRadius;
    Point itsCenter;
}

public class Square extends Shape {
    ShapeType itsType;
    double itsSide;
    Point itsTopLeft;
}


//----------------------drawAllShapes---------------------
public void drawAllShapes(ShapePointer list[], int n) {
    int i;
    for (int i=0; i<n; i++) {
        Shape s = list[i];
        switch (s.itsType) {
            case SQUARE:
                drawSquare((Square)s);
                break;
            case CIRCLE:
                drawCircle((Circle)s);
                break;
        }
    }

}

2、遵循OCP

        drawAllShapes()符合OCP,对它的改动是增加新的模块,以及为了能够实例化新类型而围绕main的改动。

public interface Shape {
    public void draw();
}

public class Square implements Shape {
    public void draw() {
        // 业务细节...
    }
}

public class Circle implements Shape {

    public void draw() {
        // 业务细节...
    }
}

//----------------------drawAllShapes---------------------
public void drawAllShape(List<Shape> list) {

    for(Shape s : list) {
        s.draw();
    }
}

3、预测变化与“合适”的结构

        假设我们要求所有的圆必须在正方形之前绘制,drawAllShapes()无法对这种变化做到封闭。

        无论模块是多么的“封闭”,都会存在一些无法对之封闭的变化。不存在对所有情况都合适的模型。既然不可能完全封闭,那么就必须有策略地对待这个问题。设计人员必须对他设计的模型应该对哪种变化封闭做出选择。他必须先猜测出最有可能发生变化种类,然后构造抽象来隔离那些变化。

        遵循OCP的代价也是昂贵的。创建正确的抽象是要花费时间和精力的。同时,那些抽象也增加了软件设计的复杂性。因而,我们希望把OCP的应用限定在可能会发生的变化上。

应对之道

1、只受一次愚弄

        谚语:“愚弄我一次,应该羞愧的是你;再次愚弄我,应该羞愧的是我。”这意味着在我们最初编写代码时,假设变化不会发生。当变化发生时,我们就创建抽象来隔离以后同类变化。

2、刺激变化

        如果我们决定接受第一颗子弹,那么子弹到来的越早越好、越快就对我们越有利。

首先编写测试——迫使系统可测试,构建了使系统可测试的抽象。

使用很短的迭代周期进行开发

在加入基础结构前就开发特性,并经常性地把那些特性展示给涉众。

首先开发最重要的特性

尽早地、经常性地发布软件

使用抽象获得显式封闭

public interface Shape extends Comparable<Shape> {
    public void draw();
    public boolean precedes(Shape s);
}

public class Circle implements Shape {
    // 根据对象类型排序...
    public boolean precedes(Shape s) {
        if (s instanceof Square) {
            return true;
        }
        return false;
    }

    public int compare(Shape s1, Shape s2) {
        if (s1.precedes(s2)) {
            return 1;
        }
        return -1;
    }
}

public void drawAllShapes(List<Shape> list) {
    sort(list);
    for (Shape s : list) {
        s.draw();
    }
}

表格驱动法获取封闭性

public class Shape {

    private static final Class<Shape>[] typeOrderTable = new Class[]{Circle.class, Square.class};

    public void draw() {
        //...
    }

    public boolean precedes(Shape s) {
        Class thisType = this.getClass();
        Class argType = s.getClass();
        boolean done = false;
        int thisOrder = -1;
        int argOrder = -1;
        for (int i=0; !done; i++) {
            Class tableEntity = typeOrderTable[i];
            if (tableEntity.equals(thisType)) {
                thisOrder = i;
            }
            if (tableEntity.equeals(argType)) {
                argOrder = i;
            }
            if (thisOrder >= 0 && argType >= 0) {
                done = true;
            }
        }
        return thisOrder < argOrder;
    }
    
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值