1、场景问题
众所周知,在Java应用开发中,需要做到“面向接口编程”,而接口的思想就是“封装隔离”,将具体的实现封装,外部调用只能通过接口调用,将外部的调用与内部实现分割开。
1.1、使用接口的好处?
内部的实现与接口隔离开,只要接口的内容不改变,就算内部实现的逻辑发生改变,也不会影响外部程序的调用(可以参考Controller调用Service接口场景,修改Impl逻辑不会影响Controller调用)。
1.2、什么时候用接口,什么时候用抽象类?
- 优先选用接口
- 既要定义子类的行为,又要为子类提供共同的方法,使用抽象类。
1.3、面向接口编程
在Java的程序设计中,通常按照三层结构划分应用,表示层,逻辑层和数据层,层级之间的调用都是通过接口调用实现的,这也是所谓的接口是被隔离部分的外观
1.4、不用设计模式的解决方式
在刚学习Java的时候,提供一个接口Api和一个实现类Impl,如何调用到这个Impl类中的实现方法呢?通常的做法就是new 一个Impl实例赋值给Api接口类型的变量。然后通过这个变量调用实现的方法。如下所示
1、Api.java
public interface Api {
void Operation(String s);
}
2、ImplA
public class ImplA implements Api {
@Override
public void Operation(String s) {
System.out.println("Impl A run " + s);
}
}
3、Main
public class Main {
public static void main(String[] args) {
Api api = new ImplA();
api.Operation("hello simpleFactory");
}
}
这段代码看似并无任何问题,接口的目的就是为了把接口Api与实现类ImplA隔离开。但是在上面程序中,客户端不仅知道了接口是Api,而且还明确的知道的实现类就是ImplA,违背了接口的封装思想。如果把new ImplA()
代码拿掉,客户端知道接口是Api,但无法知道实现类是谁,也不知道如何得到实现类,从而得不到接口对象,无法调用对象。
2、解决方案
2.1、简单工厂模式(参数判断)
使用简单工厂模式,为接口提供实现类的简单工厂。将实例的创造细节封装到工厂方法内部,客户端无需知道实现类如何创造。简单实例代码
1、Api.java
public interface Api {
void Operation(String s);
}
2、ImplA.java
public class ImplA implements Api {
@Override
public void Operation(String s) {
System.out.println("Impl A run " + s);
}
}
3、ImplB.java
public class ImplB implements Api {
@Override
public void Operation(String s) {
System.out.println("Impl B run " + s);
}
}
4、ImplFactory.java
public class ImplFactory {
public static Api newInstance(int i){
if (i == 1){
return new ImplA();
}else {
return new ImplB();
}
}
}
5、 Main.java
public class Main {
public static void main(String[] args) {
Api api = ImplFactory.newInstance(1);
api.Operation("hello simpleFactory");
}
}
以上的代码实现了简单工厂方法,在接口与实现类中间引入了ImplFactory工厂类,虽说是将new ImplA()
这行代码移动到了简单工厂里面,但是达到了在客户端不知道具体实现类的情况下获取Api接口。
通过上面的代码可以看出,工厂方法大多是用来创建实现类的,但实现方法还是由实现类完成的,所以,简单工厂内部主要实现的功能是“选择合适的实现类“。既然要实现选择,那么就需要选择的条件或者参数,选择条件或参数有以下来源:
- 由客户端传递:此方法有明显的缺点,1、客户端需明确知道每个参数背后代表的含义。2、当有其他的实现类添加之后,持续添加else判断,维护性差
- 来自于配置文件,由配置文件判断获得。
- 来自程序运行期间内存中的值
2.2、简单工厂模式(无参构造)
此方法是为了解决参数判断的工厂模式用户需知道参数背后含义的弊端,使客户端完全不需知道任何关于实现类的信息,下面使用配置文件实现简单工厂模式,但此方法不适用多实现类的场景。
1、Api.java
public interface Api {
void Operation(String s);
}
2、ImplA.java
public class ImplA implements Api {
@Override
public void Operation(String s) {
System.out.println("Impl A run " + s);
}
}
3、ImplFactory2.java
public class ImplFactory2 {
public static Api createApi(){
Properties properties = new Properties();
InputStream inputStream = null;
Api api = null;
try {
inputStream = ImplFactory2.class.getClassLoader().getResourceAsStream("SimpleFactory.properties");
properties.load(inputStream);
api = (Api) Class.forName(properties.getProperty("ImplClass")).newInstance();
} catch (IOException e) {
System.out.println("加载配置文件出错");
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return api;
}
}
4、SimpleFactory.properties
ImplClass=com.liu.simplefactory.ImplA
5、Main.java
public class Main {
public static void main(String[] args) {
Api api = ImplFactory2.createApi();
api.Operation("hello simpleFactory");
}
}
此时的客户端完全不需要知道关于接口实现类的任何细节,就可以得到接口相应的实现类,简单工厂模式还可以用(IOC/DI)实现。
3、优缺点
3.1、简单工厂模式优点
- 帮助封装:实现了组件的封装,让外部组件真正面向接口。
- 解耦:实现了客户端与实现类之间的解耦,客户端不关心实现类的创建。
3.2、简单工厂模式缺点
-
可能增加客户端复杂度
如果通过参数选择实现类,那么客户端必须知道每个参数的含义,增加了客户端使用难度。
-
不方便扩展子工厂
私有化简单工厂的构造方法,使用静态方法创造实现类,无法通过子类改变创建实现类的行为。