前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >开闭原则

开闭原则

作者头像
LieBrother
发布2019-03-29 18:15:22
4050
发布2019-03-29 18:15:22
举报
文章被收录于专栏:LieBrotherLieBrother

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

简介

姓名:开闭原则

英文名:Open Closed Principle

价值观:老顽童就是我,休想改变我

个人介绍

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.(软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的)

(来自维基百科)

停更了三四天了,这几天比较忙,不仅仅是工作上,更多是精神上。周日突然老胃病又复发了,一直疼到凌晨 4,5 点。因为这次疼得蛮厉害的,所以准备去医院看一下医生,这时候才体验到大城市就医之苦。周日晚下载了微医 App (不是做广告哈),也不知道哪家医院好,在深圳两年半还没去过医院,随便选个三甲医院:北京大学深圳医院,看了消化内科门诊的医生列表,整整这一周主任医生都预约满了,顿时很崩溃,打电话给医院预约,最快只能预约 17 号,are you kidding?App 上有个『立即问诊』功能,在线把状况告诉医生,医生一天之内接诊,需要花 60 块,我就尝试一下,没想到第二天医生回复后,说可以下午去医院看,他可以临时加号。就这样跳过了预约,直接看病,不知道你是否也苦于看病烦,可以尝试这个方法,当然,如果你有更好的方法,可以留言让更多的人了解到。

跑题了跑题了,今天是想和大家分享设计模式最后一个原则:开闭原则。这个原则要求就是允许扩展,拒绝修改。既然上面讲到看医生,那就用一个跟看病有关的例子。

故事从这里开始

小明去医院看病,医生开了阿司匹林药,小明去了收费台,付了钱,总共 20 块钱。例子的代码如下:

代码语言:javascript
复制
public class OcpTest {

    public static void main(String[] args) {
        Hospital hospital = new Hospital();
        IPatient xiaoMing = new Patient("小明");
        hospital.sellMedicine(xiaoMing);
    }

}


class Medicine {
    private String name;
    private BigDecimal price;
    public Medicine(String name, BigDecimal price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }
}

class Hospital {

    private Medicine medicine = new Medicine("阿司匹林",
                    new BigDecimal(20));
    public void sellMedicine(IPatient patient) {
        BigDecimal money = patient.pay(medicine);
        System.out.println(patient.getName() + " 花了 " + 
                money.setScale(2, BigDecimal.ROUND_UP) 
                + " 块钱买了药:" + medicine.getName());
    }

}

interface IPatient {
    String getName();
    BigDecimal pay(Medicine medicine);
}

class Patient implements IPatient{

    private String name;
    public Patient(String name) {
        this.name = name;
    }

    @Override
    public BigDecimal pay(Medicine medicines) {
        return medicines.getPrice();
    }

    @Override
    public String getName() {
        return name;
    }

}

第二天和朋友聚会聊起这事,小红说道:不对呀,前几天我在医院也拿了阿司匹林药,才 14 块钱呢。小花说:奇怪了,我买的是 16 块钱。小杰回应:怎么我买的是 18 块。怎么这药这么多个价格。小明 Google 搜了一下,发现价格跟社保有关,几个人便发现,原来他们都是“不同人”:小明没有社保,小红社保是一档,小花社保是二挡,小杰社保是三挡。(假设社保一档打 7 折,社保二挡打 8 折,社保三挡打 9 折,虚拟的哈)

发现了这秘密后,作为和 IT 工作相关的人,便讨论起医院系统具体实现是怎么实现的。小红说:这很简单呢,药品给不同人提供不同的价格。代码如下:

代码语言:javascript
复制
class Medicine {
    private String name;
    private BigDecimal price;
    public Medicine(String name, BigDecimal price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public BigDecimal getPrice1() {
        return price.multiply(new BigDecimal(0.7));
    }

    public BigDecimal getPrice2() {
        return price.multiply(new BigDecimal(0.8));
    }

    public BigDecimal getPrice3() {
        return price.multiply(new BigDecimal(0.9));
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }
}

小花说:药片本身的价格是不会变的,只是给不同人不同价格,所以可以在病人获取价钱的时候去区分。代码如下:

代码语言:javascript
复制
class Patient implements IPatient{

    private String name;
    private int level;
    public Patient(String name) {
        this.name = name;
    }

    @Override
    public BigDecimal pay(Medicine medicines) {
        if (level == 1) {
            return medicines.getPrice().multiply(new BigDecimal(0.7));
        } else if (level == 2) {
            return medicines.getPrice().multiply(new BigDecimal(0.8));
        } else if (level == 3) {
            return medicines.getPrice().multiply(new BigDecimal(0.9));
        }
        return medicines.getPrice();
    }

    @Override
    public String getName() {
        return name;
    }

}

小杰陷入了沉思。。。

小明发话:你们说的方法都可以实现,但是总感觉不对劲,如果以后有社保四挡,还是要修改原来的代码,前 2 天设计模式老师讲的开闭原则忘记了么?里面说要对扩展开放,对修改封闭。我觉得这个药片价格是因为我们人而变的,那是不是我们可以把没社保的归为一类人,一档社保的也为一类,以此类推。我觉得这样实现更好,增加多 3 类病人,分别是一档社保、二挡社保、三挡社保。代码如下:

代码语言:javascript
复制
class OneLevelSocialSecurityPatient implements IPatient {

    private String name;
    public OneLevelSocialSecurityPatient(String name) {
        this.name = name;
    }

    @Override
    public BigDecimal pay(Medicine medicine) {
        return medicine.getPrice().multiply(new BigDecimal(0.7));
    }

    @Override
    public String getName() {
        return this.name;
    }
}

class TwoLevelSocialSecurityPatient implements IPatient {

    private String name;
    public TwoLevelSocialSecurityPatient(String name) {
        this.name = name;
    }

    @Override
    public BigDecimal pay(Medicine medicine) {
        return medicine.getPrice().multiply(new BigDecimal("0.8"));
    }

    @Override
    public String getName() {
        return this.name;
    }
}

class ThreeLevelSocialSecurityPatient implements IPatient {

    private String name;
    public ThreeLevelSocialSecurityPatient(String name) {
        this.name = name;
    }

    @Override
    public BigDecimal pay(Medicine medicine) {
        return medicine.getPrice().multiply(new BigDecimal("0.9"));
    }

    @Override
    public String getName() {
        return this.name;
    }
}

    // 测试代码
    public static void main(String[] args) {
        Hospital hospital = new Hospital();
        IPatient xiaoMing = new Patient("小明");
        hospital.sellMedicine(xiaoMing);
        IPatient xiaoHong = new OneLevelSocialSecurityPatient("小红");
        hospital.sellMedicine(xiaoHong);
        IPatient xiaoHua = new TwoLevelSocialSecurityPatient("小花");
        hospital.sellMedicine(xiaoHua);
        IPatient xiaoJie = new ThreeLevelSocialSecurityPatient("小杰");
        hospital.sellMedicine(xiaoJie);
    }

代码:

https://github.com/1CSH1/DesignPatterns/blob/master/src/com/liebrother/designpatterns/ocp/OcpTest.java

看了他们的对话和代码,是不是能知道哪种方式更好了?对于小红来说,她没理清价格变化的原因,价格变化不在于药片;小花理清了,但是实现方式差了点,以后如果新增了四挡社保,她的实现要修改原有的代码,不符合开闭原则;小明的方法就符合开闭原则,如果新增四挡社保人员,他的方法只需要再额外扩展一个四挡社保人员就可以,不用动用其他代码。

用了这个大家可能不太喜欢的看病的场景来描述这个开闭原则,不要忌讳哈,希望大家都健健康康,远离医院。

总结

重申一下:对扩展开放,对修改封闭。如果有同学经常看一些开源框架源码就会发现,有很多很多抽象类和接口,debug 进去很绕,其实这些抽象类和接口很多都是为了扩展用,因为作为开源框架,不得不实现各种可想象到的方案,而这些都基于开闭原则来实现的。以后有机会也可以写一下源码的文章分享给大家。

参考资料:《大话设计模式》、《Java设计模式》、《设计模式之禅》、《研磨设计模式》、《Head First 设计模式》

这周事情比较多,更新会不及时,周五还要出差去一趟上海,周六回深圳,周日回一趟老家,各种奔波。。。

希望文章对您有所帮助,设计模式系列会持续更新,感兴趣的同学可以关注公众号,第一时间获取文章推送阅读,也可以一起交流,交个朋友。

本文参与?腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-01-09,如有侵权请联系?cloudcommunity@tencent.com 删除

本文分享自 LieBrother 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com