从零开始学习Java设计模式 | 软件设计原则篇:开闭原则
Posted 李阿昀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始学习Java设计模式 | 软件设计原则篇:开闭原则相关的知识,希望对你有一定的参考价值。
从本讲开始,咱们就要开始学习第一章中的第三部分内容,即软件设计原则了。
在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据6条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。
那么是6条原则呢?看过我前面文章的同学肯定知道,就是下面这6条原则。
- 开闭原则
- 里氏代换原则
- 依赖倒转原则
- 接口隔离原则
- 迪米法特原则
- 合成复用原则
本讲我会为大家介绍第一个软件设计原则,即开闭原则。
概述
什么是开闭原则呢?开闭原则就是对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。
不妨我就举一个现实生活中的例子来说明一下开闭原则吧!在现实生活中,我们可以经常看到这样的一些效果,咱们的笔记本都会预留有一些USB接口,这样,不管是U盘还是一些什么其他的外接设备,例如鼠标、键盘,我们都可以随插随用了,即实现了一个热插拔的效果,也更加方便我们进行扩展。
有些同学可能就会问了,如果要想达到这样一个效果,那么应该怎么办呢?我们需要使用接口和抽象类。为什么要用接口和抽象类呢?因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。
那么也就是说,我们现在只须去定义一个接口或者抽象类,很显然,它们里面定义了所有子类必须重写的方法,也即定义了一些规范,然后我们再去定义其子类,这样,在后期如果我们要进行程序的扩展,那么我们就不需要再去修改原有的代码,而是直接重新再去定义一个子实现类就可以了。
可能我说了这么多,大家还不是特别理解,没关系,下面我会以搜狗输入法的皮肤为例来向大家介绍开闭原则的一个应用。
案例
这块我是以搜狗输入法的皮肤设计为例来介绍开闭原则的一个应用的。
案例分析
搜狗输入法的皮肤是输入法背景图片、窗口颜色和声音等元素的组合。用户可以根据自己的喜爱更换自己的输入法的皮肤,当然,用户可以用默认的,也可以用其他的皮肤,而且他还可以从网上下载新的皮肤。这些皮肤有共同的特点,可以为其定义一个抽象类(AbstractSkin),大家一定要注意,不管是默认的皮肤还是什么其他的皮肤,我们现在的做法就是向上进行抽取,抽取出一个抽象类或者接口,不过这一块咱们使用的是抽象类,如此一来,每一个具体的皮肤(例如DefaultSkin和MeimeixiaSkin)都将是其子类,这样,用户窗体就可以根据需要选择或者增加新的主题,而不再需要修改原代码了。
这样一路分析下来,我们可以发现该案例是能够满足开闭原则的,所以现在咱们就可以通过该案例来演示一下开闭原则了。当然了,在演示之前,大家得能看懂下面的类图,这也是通过以上分析画出来的。
从以上类图中可以看到,顶层有一个抽象的皮肤类,它下面有两个子类,一个是默认的皮肤类,一个是美美侠皮肤类,它俩都需要去重写父类中的抽象方法。当然了,旁边还有一个搜狗输入法类,由于该类拥有显示皮肤的功能,所以它需要用到皮肤,故你能看到我在这里面进行了一个聚合的操作。
当然了,以上类图中还缺少一个测试类,只不过我没有在以上类图中体现出来罢了。
案例实现
接下来,我们便通过代码来实现一下搜狗输入法皮肤设计这个案例。
要想做到这点,咱们首先得打开咱们的开发工具,这里我使用的开发工具是IDEA,相信大家也都是用的它,然后创建一个maven工程,名字不妨起为design_patterns,以后有关设计模式的代码我们统统都会放在这个工程里面,工程创建好之后,不妨接着在其下创建一个包,例如com.meimeixia.principles,在该包下存放的都是我们编写的有关软件设计原则的代码,至于设计模式的代码,后续我会重新再创建一个包来进行存放。
当然了,软件设计原则有多条,要想讲清楚每一条设计原则,我们还得在com.meimeixia.principles包下分别创建多个子包来存放每一条设计原则所对应案例的代码,例如com.meimeixia.principles.demo1包里面存放的就是开闭原则所对应案例(即搜狗输入法的皮肤设计)的代码。
下面,我们就得根据以上类图来写出对应的代码了。
首先,定义一个抽象皮肤类,例如AbstractSkin。
package com.meimeixia.principles.demo1;
/**
* 抽象皮肤类
* @author liayun
* @create 2021-05-27 12:52
*/
public abstract class AbstractSkin {
// 显示的方法
public abstract void display();
}
然后,再来定义以上抽象皮肤类的子类,咱们定义的第一个子类是默认皮肤类,即DefaultSkin。
package com.meimeixia.principles.demo1;
/**
* 默认皮肤类
* @author liayun
* @create 2021-05-27 12:53
*/
public class DefaultSkin extends AbstractSkin {
@Override
public void display() {
System.out.println("默认皮肤");
}
}
咱们定义的第二个子类是美美侠皮肤类,即MeimeixiaSkin。
package com.meimeixia.principles.demo1;
/**
* 美美侠皮肤类
* @author liayun
* @create 2021-05-27 12:55
*/
public class MeimeixiaSkin extends AbstractSkin {
@Override
public void display() {
System.out.println("美美侠皮肤");
}
}
以上三个类定义完毕之后,我们还得定义一个类,从以上类图可知,该类的类名叫SougouInput,此外,我们还知道该类聚合了抽象皮肤类。这样,该类的代码应该是下面这样子的。
package com.meimeixia.principles.demo1;
/**
* 搜狗输入法
* @author liayun
* @create 2021-05-27 12:56
*/
public class SougouInput {
private AbstractSkin skin;
public void setSkin(AbstractSkin skin) {
this.skin = skin;
}
public void display() {
skin.display();
}
}
可以看到,我在SougouInput类里面定义了一个AbstractSkin类型的成员变量,这就代表了SougouInput类聚合了抽象皮肤类。此外,由于外界要对AbstractSkin类型的成员变量赋值,所以我们还得提供对应的set方法,当然,这里面我没有提供get方法,只是提供了一个set方法用于赋值。最后,在SougouInput类里面我还提供了一个display方法,该方法是用于展示皮肤的,大家注意了,在该方法里面是调用抽象皮肤类的具体子类的display方法去进行皮肤的一个展示的。
SougouInput类定义完之后,别着急,还有一个类需要我们去定义,它就是咱们的测试类,我们可以给它起名为Client。下面我提供了一个编写好的测试类,以供大家参考。
package com.meimeixia.principles.demo1;
/**
* @author liayun
* @create 2021-05-27 12:57
*/
public class Client {
public static void main(String[] args) {
// 1. 创建搜狗输入法对象
SougouInput input = new SougouInput();
// 2. 创建皮肤对象
// DefaultSkin skin = new DefaultSkin();
MeimeixiaSkin skin = new MeimeixiaSkin();
// 3. 将皮肤设置到输入法中
input.setSkin(skin);
// 4. 显示皮肤
input.display();
}
}
相信大家都能看懂以上测试类中编写的代码,我就不再详细赘述了。
测试类定义完毕之后,我们来测试一下,看效果是不是我们所想要的,如下图所示,美美侠皮肤打印出来了,看来效果确实是达到了。
如果现在我们不想用美美侠皮肤了,而是想要用默认皮肤,那么只须稍微修改一下测试类即可,怎么修改想必大家都知道了,我也就不再赘述了。这时,你会发现其他四个类的代码都没有进行任何修改,效果便已经实现了。
后期我们可能面临这样一个需求,就是我们还想再去扩展一种皮肤,例如觉醒年代皮肤,那么你觉得现在以上这个案例有没有满足开闭原则呢?很显然,是满足的,你要知道开闭原则是对扩展开放,对修改关闭的,也就是说我们要进行扩展的话是可以的,但是尽量不要去修改我们之前编写的类的代码。
如果现在还想再去扩展一种觉醒年代皮肤,那么此时我们的做法就很简单了,重新再创建一个类,例如JueXingNianDaiSkin,然后让它去继承抽象皮肤类即可。接下来的事情就好办了,只需要把客户端的代码进行一个修改,让其使用觉醒年代皮肤,一切就都OK了。
至此,开闭原则我就为大家介绍到这。
以上是关于从零开始学习Java设计模式 | 软件设计原则篇:开闭原则的主要内容,如果未能解决你的问题,请参考以下文章
从零开始学习Java设计模式 | 软件设计原则篇:里氏代换原则
从零开始学习Java设计模式 | 软件设计原则篇:里氏代换原则
从零开始学习Java设计模式 | 软件设计原则篇:合成复用原则
从零开始学习Java设计模式 | 软件设计原则篇:合成复用原则