所谓设计模式:在软件开发中,经过验证的,用于解决在特定环境下,重复出现的,特定问题的解决方案.
对设计模式的理解:
- 设计模式是解决某些问题的办法.
- 设计模式不是凭空想象出来的,是经验的累积和总结.
- 结构型模式:描述如何组织类和对象,以获得更大的结构.
- 行为型模式:描述算法和对象间职责的分配.
学习设计模式第一步:准确理解每一个设计模式的功能,结构,标准实现,了解适合使用它的场景以及使用效果.
第二步:在实际开发中,尝试使用这些设计模式,并反复思考总结使用是否得当,是否需要做一些变化.
1. 简单工厂
严格来说,简单工厂并不属于标准的设计模式.但是很常用.所以可以作为热身来掌握.
1.1接口回顾
- 接口的概念:接口是一种特殊的抽象类,跟一般的抽象类相比,接口里所有方法都是抽象方法,接口里所有属性都是常量.
- 接口用来干什么:接口是用来定义实现类的外观,也就是实现类的行为定义.用来约束实现类的行为.
- 接口的思想:接口的思想就是"封装隔离".
封装通常的封装指的是对数据的封装.这里的封装指的是:对被隔离体的行为(或者说职责)的封装.
隔离指的是:外部调用和内部实现的隔离,外部调用只能通过接口进行调用,外部调用时不知道内部实现的.也就是说外部调用和内部实现是被接口隔离开的. - 接口的好处:由于外部调用和内部实现是隔离开的,只要接口不变,内部实现的变化是不会影响外部调用的,从而使系统更加灵活,更好的扩展性和维护性.这也就是接口是系统可插拔性的保证这句话的意思
- 接口和抽象类的选择:
1.优先选择接口.2.既要定义子类的行为,有要为子类提供功能的功能时选择抽象类.
1.2 面向接口编程
java程序设计里面,非常讲究层的划分和模块的划分.通常按照三层来划分java程序:表现层,逻辑层,数据层.他们之间都要通过接口来通信.
在每一层有很多小模块,每个小模块对外则是一个整体.所以一个模块应该对外提供接口,当需要使用这个模块的功能时,可以通过这个接口来调用.这就是常说的:接口是其被隔离部分的外观.
既然java中需要面向接口编程,那么在程序中如何使用接口来做到面向接口编程呢?
1.3 场景问题
我们是怎么使用接口?假设有一个接口HelloApi,然后有一个实现类HelloApiImpl,然后创建一个实现类对象,那么客户端怎么用呢?
通常都是在客户端创建一个实现类对象实例,把它赋值给一个HelloApi接口类型的变量.然后客户端就可以通过这个变量来操作接口的功能了.代码示例:
public interface HelloApi {
void hello(String s);
}
-------------------------
public class HelloApiImpl implements HelloApi {
@Override
public void hello(String s) {
System.out.println("this is helloApiImpl..." + s);
}
}
----------------------------------------
public class Test {
public static void main(String[] args) {
HelloApi helloApi = new HelloApiImpl();
helloApi.hello("hello world");
}
}
有什么问题?
客户端调用的时候:HelloApi helloApi = new HelloApiImpl();
客户端不仅知道了接口,还知道具体实现类就是HelloApiImpl.接口的思想是封装隔离,而实现类应该是被接口HelloApi封装并与客户端隔离开的,也就是说客户端根本不应该知道实现类就是HelloApiImpl.
1.4 解决方案
什么是简单工厂:提供创建一个具体实例的功能,而无需关心起具体实现.被创建的实例的类型可以是接口,抽象类,也可以是具体的类.
使用简单工厂来解决上述问题:
public interface HelloApi {
void hello(String s);
}
-------------------------
public class HelloApiImpl implements HelloApi {
@Override
public void hello(String s) {
System.out.println("this is helloApiImpl..." + s);
}
}
----------------------------
public class HelloApiFactory {
public static HelloApi create(){
// 如果HelloApi接口有多种实现,可以通过参数传入条件
// 根据不同条件,创建不同实现类.本例就一个实现类,所以就简单省略了.
return new HelloApiImpl();
}
}
------------------------------
public class Test {
public static void main(String[] args) {
// 重要改变,没有new HelloApiImpl了,如同上面
HelloApi helloApi = HelloApiFactory.create();
helloApi.hello("hello world");
}
}
如同上面的示例,客户端通过简单工厂创建了一个实现接口的对象,然后面向接口编程.从客户端来看,他根本不知道具体的实现是什么,如何实现.它只知道通过简单工厂获得了一个接口对象,然后通过这个接口获取想要的功能.
事实上,简单工厂能够真正的帮助我们面向接口编程.之前的做法,其实只是用到了接口的多态,而最重要的"封装隔离性"并没有体现出来.
1.5模式讲解
典型疑问:简单工厂不就是把new HelloApiImpl()从客户端移到了简单工厂吗?把这句代码放到客户端和放到简单工厂有什么区别吗?
从前面的学习我们知道,接口是用来封装隔离具体实现的,目标就是不让客户端知道封装体内部的具体实现.简单工厂是位于封装体内的,是和接口,具体实现类在一起的,算是封装体内部的一个类.简单工厂结构图:
虚线框好比是一个组件的包装边界,表示接口,实现类,简单工厂组成了一个组件.在这个封装体里面,只有接口和工厂是对外的.
1.6 认识简单工厂
使用简单工厂的时候,通常不用创建工厂的类实例,所以把简单工厂类实现成一个工具类,直接使用静态方法就可以了.所以也被称为静态工厂.
理论上来讲,工厂什么都可以创建.但是对于简单工厂可创建对象的范围,通常不要太大,建议控制在一个组件级别或一个模块级别.否则工厂会职责不明,像一个大杂烩.
简单工厂类名建议"模块名称+Factory".方法名称建议"get+接口名称".
虽然说简单工厂的方法大多是用来创建接口的,但是真正实现功能的是接口的实现类,这些类是已经做好的,并不是要靠简单工厂来创建.所以简单工厂的方法的内部主要实现的功能是选择合适的实现类
简单工厂的本质:选择实现. 实现简单工厂的难点就在于"如何选择"实现.
何时选择简单工厂?
1.如果想要完全封装隔离具体实现,让外部只能通过接口来操作封装体.
2.如果想要把对外创建对象的职责集中管理和控制.
相关设计模式: