设计模式学习之——六大设计原则之六:开闭原则

    定义:Software entities like classes, modules and functions should be open for extension but closed for modifications.
    (一个软件实体如类、模块和函数应该对扩展开放,对修改关闭)

直接上例子:

    如上IBook定义了数据的三个属性,名称,价格,作者。小说类NovelBook是一个具体的实现类,是所有小说的总称

IBook类:
public interface IBook
{
    string getName();
    int getPrise();
    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;
    }
    public String getName()
    {
        return this.author;
    }
 
    public int getPrise()
    {
        return this.price;
    }
 
    public String getAuthor()
    {
        return this.author;
    }
}
    注意:这里价格为int并不是错误的,通常,在非金融类项目中对货币的处理时,一般取2位精度,通常的设计方法是在运算过程中扩大100倍,在需要展示的时候再缩小100倍,减少精度带来的误差。

书店售书类:
public class BookStore
{
    private final static ArrayList<IBook> bookList= new ArrayList<IBook>();
    //static 静态模块初始化数据,实际项目中由持久层完成
    static{
        bookList.add(new NovelBook("天龙八部",3200,"金庸"));
        bookList.add(new NovelBook("巴黎圣母院",5600,"雨果"));
    }
    public static void main(String[] args){
        NumberFormat formatter = NumberFormat.getCurrencyInstance();
        formatter.setMaxmumFractionDigits(2);
        System.out.printIn("-----------书店卖出去的书记录如下:--------------");
        for(IBook book: bookList){
        System.out.printIn("书名:" + book.getName() + "\t作者:" + book.getAuthor() + "\t价格:" + formatter.format(book.)getPrice()/100.0) + "元");
        }
    }
}
    现在40元以上的书需要打9折,其他的8折。这对于已经投产的项目来说,是个变化,如何修改?
解决1:
    修改接口:在IBook上新增方法getOffPrice(),专门用于打折处理,所有实现类实现该方法。但是这样修改的后果是,实现类NovelBook要修改,BookStore中的main方法也要修改,同时IBook既然是作为接口,则应该是稳定且是可靠的,不应该经常发生变化。所以不行!
解决2:
    修改实现类:修改NovelBook类中的方法,直接在getPrice()中实现打折处理,好办法,这个应该是经常使用的。该方法在项目有明确的团队约束或优良的架构设计时,是非常优秀的方法,但是该方法还是有缺陷。
    例如采购部门也要看价格,由于该价格已经实现了打折处理后的价格,所以采购看到的也是折后的价格,导致信息不对称。所以 不是最优的方案。
解决3:
    通过扩展实现变化:增加子类OffNovelBook,重写getPrice方法,高层次模块(static静态模块区)通过OffNovelBook类产生新的对象,完成业务变化对系统的最小化开发。类图如下:

打折销售的小说类:
public class OffNovelBook : NovelBook
{
    public OffNovelBook(string _name, int _price, string _author)
    {
        super(_name, _price, _author);
    }
    @Override
    public int getPrice()
    {
        int selfPrice = super.getPrice();
        int offPrice = 0;
        if (selfPrice > 4000){
            offPrice = selfPrice * 90 / 100;
        }
        else {
            offPrice = selfPrice * 80 / 100;
        }
        return offPrice;
    }
}
修改书店打折销售类:
static{
    bookList.add(new OffNovelBook("天龙八部",3200,"金庸"));
    bookList.add(new OffNovelBook("巴黎圣母院",5600,"雨果"));
}
仅仅修改上述代码。

我们将变化归纳为三种类型:
1. 逻辑变化:
    只修改逻辑不涉及其他模块,如:a*b+c改成了a*b*c。可以通过修改原有类中的方法的方式来完成,前提是所有依赖或关联类都按照相同的逻辑处理。
2. 子模块变化:
    一个模块变化会影响其他模块。如上述例子。
3. 可见视图变化:
    给客户使用的界面,该部分变化会引起连锁反应。但是还是可以按照扩展来完成变化。


    开闭原则是最基础的一个原则,前面的五个原则都是开闭原则的具体形态,也就是说前五个原则就是指导设计的工具和方法,而开闭原则才是精神领袖。换个角度理解:从Java语言的称谓:开闭原则是抽象类,其他五大原则是实现类。

如何使用开闭原则?
1. 抽象约束
    a. 约束扩展,对扩展边界限定,不允许出现在接口和抽象中不存在的public方法
    b. 参数类型、引用对象尽量使用接口或者抽象类,而不是实现类
    c. 抽象层保持稳定,确定以后不可修改
2. 元数据(metadata)控制模块行为
    元数据:配置参数
3. 制定项目章程
4. 封装变化
    a. 将相同的变化封装到一个接口或抽象类中
    b. 将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。


后记:
        设计模式的六大原则基本讲完了,这些都是看书笔记,方便后续的阅读。还有后续的23种设计模式,最近肯定是没时间看了。终于是体会到了去年的前同事跟我说的“时间不够用”的感觉了。今年春节以后,基本没有在晚上两点前睡过,每天都学到那么晚,不过也不知道到底自己Android学到什么程度了。
        时间去哪儿了,只能来个排排序了:Android是毫无疑问的,准备继续在这上面投入全部精力。后续可能的话还想学习Javascript,Python,还有设计模式。当然了,如果有钱Object-C还是很想摸摸的。不过这些都是后话了,等到Android能做出一个牛逼哄哄的应用出来再说吧。
        最近弄公司的项目也终于在今天晚上把同事交给我的两个模块的功能给完成了,难度虽然一般,不过里面的干货是相当的多啊。花点时间整理一下。

over


欢迎转载,转载注明出处,谢谢
Mr.傅:阅读自《设计模式之禅》

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值