设计模式笔记(六)设计六大原则之六--开闭原则

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cc907566076/article/details/78597964

定义

一个软件实体如类,模块和函数应该对扩展开放,对修改关闭。即一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。

软件实体:
项目或软件产品中按照一定的逻辑规则划分的模块
抽象和类
方法

示例

public interface IBook {
    //书籍有名称
    public String getName();
    //书籍有售价
    public int getPrice();
    //书籍有作者
    public String getAuthor();
}
public class NovelBook implements IBook{
    //书籍名称
    private String name;
    //书籍价格
    private int price;
    //书籍的作者
    private String author;

    //通过构造函数传递书籍数据
    public NovelBook(String _name,int _price,String _author) {
        this.name = _name;
        this.price = _price;
        this.author = _author;
    }

    @Override
    public String getName() {
        // TODO Auto-generated method stub
        return this.name;
    }

    @Override
    public int getPrice() {
        // TODO Auto-generated method stub
        return this.price;
    }

    @Override
    public String getAuthor() {
        // TODO Auto-generated method stub
        return this.author;
    }

}
/**
 * 书店售书类
 * @author Administrator
 *
 */
public class BookStore {
    private final static ArrayList<IBook> bookList = new ArrayList<IBook>();

    //static 静态模块初始化,项目中一般是通过持久层完成
    static {
        bookList.add(new NovelBook("天龙八部",3200,"金庸"));
        bookList.add(new NovelBook("巴黎圣母院",5600,"雨果"));
        bookList.add(new NovelBook("悲惨世界",3500,"雨果"));
        bookList.add(new NovelBook("金瓶梅",4300,"兰陵笑笑生"));

    }

    //模拟书店买书
    public static void main(String[] args) {
        NumberFormat formatter = NumberFormat.getCurrencyInstance();
        formatter.setMaximumFractionDigits(2);
        System.out.println("---------书店卖出去的书籍记录如下:-----------");
        for(IBook book : bookList) {
            System.out.println("书籍名称:"+book.getName()+"\t书籍作者:"+book.getAuthor()+
                    "\t书籍价格:"+formatter.format(book.getPrice()/100)+"元");
        }
    }
}

运行结果:
———书店卖出去的书籍记录如下:———–
书籍名称:天龙八部 书籍作者:金庸 书籍价格:¥32.00元
书籍名称:巴黎圣母院 书籍作者:雨果 书籍价格:¥56.00元
书籍名称:悲惨世界 书籍作者:雨果 书籍价格:¥35.00元
书籍名称:金瓶梅 书籍作者:兰陵笑笑生 书籍价格:¥43.00元

此时如果需求变化,希望40元以上的书籍九折销售,其他8折销售,那么我们如何应对呢?

1.修改接口?
如果在IBook上新增一个getOffPrice()的方法,专门用于进行打折处理,这样结果实现类NovelBook要修改,main方法也要修改。而且接口IBook应该是稳定且可靠的,不应该经常发生变化,否则接口作为契约的作用就失去了效能

2.修改实现类?
修改NovelBook类中的方法,直接在getPrice()中实现打折处理,一般项目中经常使用的就是这种方法(缺陷修复),但是如果采购书籍的人员也要看价格呢?那么他看到的就是打折后的价格了,显然也不正确。

3.通过扩展实现变化
增加一个子类OffNovelBook,覆写getPrice方法,完成业务变化对系统的最小化开发。风险小。

public class OffNovelBook extends NovelBook{

    public OffNovelBook(String _name, int _price, String _author) {
        super(_name, _price, _author);
        // TODO Auto-generated constructor stub
    }

    //覆写销售价格
    @Override
    public int getPrice() {
        //原价
        int selfPrice = super.getPrice();
        int offPrice = 0;
        if(selfPrice > 40) {
            offPrice = selfPrice * 90/100;
        }else {
            offPrice = selfPrice * 80/100;
        }
        return offPrice;
    }

}

这样业务逻辑只需要修改static块中的构造书籍,其他并没有任何改动。

bookList.add(new OffNovelBook("天龙八部",3200,"金庸"));
bookList.add(new OffNovelBook("巴黎圣母院",5600,"雨果"));
bookList.add(new OffNovelBook("悲惨世界",3500,"雨果"));
bookList.add(new OffNovelBook("金瓶梅",4300,"兰陵笑笑生"));

运行结果显示:
———书店卖出去的书籍记录如下:———–
书籍名称:天龙八部 书籍作者:金庸 书籍价格:¥28.00元
书籍名称:巴黎圣母院 书籍作者:雨果 书籍价格:¥50.00元
书籍名称:悲惨世界 书籍作者:雨果 书籍价格:¥31.00元
书籍名称:金瓶梅 书籍作者:兰陵笑笑生 书籍价格:¥38.00元

总结

逻辑变化

只变化一个逻辑,不涉及其他模块,可以通过修改原有类中的方法来完成,前提条件是所有依赖或关联类都按照相同的逻辑处理。

子模块变化

一个模块变化,会对其他模块产生影响,特别是一个低层次的模块变化必然会引起高层模块的变化,因此在通过扩展完成变化时,高层次的模块修改时必然的。

扫描二维码关注公众号,回复: 3569824 查看本文章

可见视图变化

提供给客户使用的视图,如jsp等,该部分变化一般会引起连锁反应。如果一个展示数据列表,现在增加显示一列,这一列要跨很多张表,处理很多逻辑才能展现出来,这样的变化时比较恐怖的。还是可以通过扩展来完成,这要看原有的设计是否灵活。

为什么采用开闭原则

测试逻辑清晰

单元测试时只需要保证此新加的类正确就可以了,非常清晰。

可以提高复用性

逻辑颗粒度越小,被复用的可能性就越大。

提高可维护性

对原有代码修改,要先读懂源码,是一件很痛苦的事情。通过扩展,大大提高了维护人员的可维护性。

面向对象开发要求

设计开始就要考虑有可能变化的因素,留下接口,等待可能变为现实。

如何使用开闭原则

抽象约束

通过接口或抽象类可以约束一组可能变化的行为,并且能够对扩展开放。比如上面实例中还可以增加艺术书籍,技术书籍,水利工程书籍等等。只需要扩展接口,其他的水到渠成。

元数据控制模块行为

元数据:配置参数,可以从文件中获得,也可以从数据库中获得,用来描述环境和数据的数据。比如login方法中提供了这样的逻辑,先检查IP地址是否在允许访问的列表中,然后再决定是否需要到数据库中验证密码。

制定项目章程

比如SSH项目开发,bean配置文件非常多,如果扩展,就需要增加子类,并修改配置文件。然而如果制定了一个项目章程,规定所有Bean都自动注入等等。这就需要项目内约束。

封装变化

将相同的变化封装到一个接口或抽象类中,将不同的变化封装到不同的接口或抽象类中。

猜你喜欢

转载自blog.csdn.net/cc907566076/article/details/78597964
今日推荐