工厂模式
概念:
实例化对象,用工厂方法代替new操作
工厂模式包括工厂方法模式和抽象工厂模式
抽象工厂模式是工厂方法模式的扩展
意图:
定义一个接口来创建对象,但是让子类来决定哪些类需要被实例化。
工厂方法把实例化的工作推迟到子类中去实现。
适合工厂模式的情况:
1.有一组类似的对象需要创建。
2.在编码的时候不能预见需要创建哪种类的实例。
3.系统需要考虑扩展性,不应依赖于产品类实例化如何被创建,组合和表达的细节。
项目现状:
在软件系统中经常面临着“对象”的创建工作,由于需求的变化,这个对象可能随之也会发生变化,但它却拥有比较稳定的接口。为此,我们需要提供一种封装机制来隔离出这个易变对象的变化,从而保持系统中其他依赖该对象的对象不随着需求变化而变化。
设计:
1.尽量松耦合,一个对象的依赖对象的变化与本身无关
2.具体产品与客户端剥离,责任分割
例如常见的JDBC接口,Spring的Bean工厂
eg1:拍照软件中的发型样式设计
/**
* 发型接口
*/
public interface HairInterface {
/**
* 发型展示
*/
public void show();
}
/**
* 左偏发型
*/
public class LeftHair implements HairInterface{
@Override
public void show() {
System.out.println("左偏发型");
}
}
/**
* 右偏发型
*/
public class RightHair implements HairInterface {
@Override
public void show() {
System.out.println("右偏发型");
}
}
通常的使用方法:直接在外部根据所要使用的发型去new一个对象。在这种方法中用户获取发型对象这一操作是自己在外部取操作的,随着发型种类的增加,或者修改,这一操作方式就会存在问题了。
private static void FactoryTestByInterface() {
HairInterface lefthair=new LeftHair();
lefthair.show();
HairInterface righthair=new RightHair();
righthair.show();
}
建立发型工厂,把实例化的工作交给工厂去做,用户只需知道怎么取就行。
/**
* 发型工厂
*/
public class HairFactory {
/**
* 通过关键字获取发型实例
* @param key 关键字
* @return HairInterface对象
*/
public static HairInterface getHairByKey(String key){
HairInterface hair = null;
if("LeftHair".equals(key)){
hair=new LeftHair();
}
else if("RightHair".equals(key)){
hair=new RightHair();
}
return hair;
}
}
private static void FactoryTestByKey() {
String key;
key="LeftHair";
HairInterface lefthair=HairFactory.getHairByKey(key);
lefthair.show();
key="RightHair";
HairInterface righthair=HairFactory.getHairByKey(key);
righthair.show();
}
在发型工厂中通过 if-else 对关键字的判断来识别用户需要什么类型的对象,但随着发型种类的增加 if-else 需要嵌套很多。
下面通过类名反射获取发型对象
/**
* 通过类名反射获取发型实例
* @param className 类名
* @return HairInterface对象
*/
public static HairInterface getHairByClass(String className){
HairInterface hair = null;
try {
Class<?> hairclass=Class.forName(className);
hair=(HairInterface) hairclass.newInstance();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return hair;
}
private static void FactoryTestByClass() {
String className;
className="com.lmr.factory.LeftHair";
HairInterface lefthair=HairFactory.getHairByClass(className);
lefthair.show();
className="com.lmr.factory.RightHair";
HairInterface righthair=HairFactory.getHairByClass(className);
righthair.show();
}
由于类名太长,在使用时可能会造成不便。
下面通过资源文件实现映射来缩短关键字参数。
/**
* 资源工具类
*/
public class PropertiesTool {
/**
* 获取Properties资源文件内容,以键值对的方式存入map中
* @param file Properties资源文件名
* @return Properties资源文件内容的Map集合
*/
public static Map<String, String> getPropertiesMap(String file){
Map<String, String> map = new HashMap<String, String>();
Properties properties=new Properties();
try {
InputStream stream=PropertiesTool.class.getResourceAsStream(file);
properties.load(stream);
Enumeration<?> en=properties.propertyNames();
while (en.hasMoreElements()) {
String key = (String) en.nextElement();
String value=properties.getProperty(key);
map.put(key, value);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return map;
}
}
HairTypeClass.properties资源文件
//HairTypeClass.properties
LeftHair=com.lmr.factory.LeftHair
RightHair=com.lmr.factory.RightHair
/**
* 通过关键字读取资源文件获取类名来得到发型实例
* @param classKey 关键字
* @return HairInterface对象
*/
public static HairInterface getHairByClassKey(String classKey){
HairInterface hair = null;
Map<String, String> map=PropertiesTool.getPropertiesMap("HairTypeClass.properties");
String className=null;
className=map.get(classKey);
try {
Class<?> hairclass=Class.forName(className);
hair=(HairInterface) hairclass.newInstance();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return hair;
}
private static void FactoryTestByClassKey() {
String key;
key="LeftHair";
HairInterface lefthair=HairFactory.getHairByClassKey(key);
lefthair.show();
key="RightHair";
HairInterface righthair=HairFactory.getHairByClassKey(key);
righthair.show();
}
运行结果:
左偏发型
右偏发型
抽象工厂
抽象工厂就是有一个工厂类接口,有多个具体的工厂类继承这个工厂类接口,获取工厂类需要制定具体调用哪一个工厂类实例,然后调用这个工厂类中的方法获取指定的对象。
假设一个子系统需要一些产品对象,而这些产品又属于一个以上的产品等级结构。那么为了将消费这些产品对象的责任和创建这些产品对象的责任分割开来,可以引进抽象工厂模式。这样的话,消费产品的一方不需要直接参与产品的创建工作,而只需要向一个公用的工厂接口请求所需要的产品。
通过使用抽象工厂模式,可以处理具有相同(或者相似)等级结构中的多个产品族中的产品对象的创建问题。
eg2:游戏中每到过节的时候,会出各种各样的人物套装。比如,新年套装,圣诞套装。
/**
* 男孩接口
*/
public interface BoyInterface {
/**
* 套装展示
*/
public void show();
}
/**
* 新年男孩套装
*/
public class HNBoy implements BoyInterface {
@Override
public void show() {
System.out.println("新年男孩套装展示");
}
}
/**
* 圣诞男孩套装
*/
public class MCBoy implements BoyInterface {
@Override
public void show() {
System.out.println("圣诞男孩套装展示");
}
}
/**
* 女孩接口
*/
public interface GirlInterface {
/**
* 套装展示
*/
public void show();
}
/**
* 新年女孩套装
*/
public class HNGirl implements GirlInterface {
@Override
public void show() {
System.out.println("新年女孩套装展示");
}
}
/**
* 圣诞女孩套装
*/
public class MCGirl implements GirlInterface {
@Override
public void show() {
System.out.println("圣诞女孩套装展示");
}
}
建立人物套装工厂
/**
* 人物套装工厂接口
*/
public interface PersonFactoryInterface {
/**
* 获取女孩套装
* @return GirlInterface对象
*/
public GirlInterface getGril();
/**
* 获取男孩套装
* @return BoyInterface对象
*/
public BoyInterface getBoy();
}
/**
* 新年套装工厂
*/
public class HNFactory implements PersonFactoryInterface {
@Override
public GirlInterface getGril() {
return new HNGirl();
}
@Override
public BoyInterface getBoy() {
return new HNBoy();
}
}
/**
* 圣诞套装工厂
*/
public class MCFactory implements PersonFactoryInterface {
@Override
public GirlInterface getGril() {
return new MCGirl();
}
@Override
public BoyInterface getBoy() {
return new MCBoy();
}
}
使用:首先获取对应节日的套装工厂,然后在通过该工厂去获取不同的人物套装对象。
private static void AbstractFactoryTestOne() {
PersonFactoryInterface personFactory1=new HNFactory();
BoyInterface boy=personFactory1.getBoy();
boy.show();
PersonFactoryInterface personFactory2=new MCFactory();
GirlInterface girl=personFactory2.getGril();
girl.show();
}
结果:
新年男孩套装展示
圣诞女孩套装展示
工厂方法和抽象工厂方法的对比:
1.工厂模式是一种极端情况下的抽象工厂模式,通过前面的类图和代码实现我们可以看到这样一个对比,而抽象工厂模式可以看成是工厂模式的推广
2.工厂模式用来创建一个产品的等级结构,而抽象工厂模式是用来创建多个产品的等级结构
3.工厂模式只有一个抽象产品类,而抽象工厂模式有多个抽象产品类,总之就是单一对多个产品的这种对比
工厂模式帮助我们实现了什么呢
1.系统可以在不修改具体工厂角色的情况下引进新的产品
2.客户端不必关系对象如何去创建,明确了职责。对象具体的创建交给了具体的产品,product1,product2,客户端只要告诉工厂我需要哪一个产品,product1还是product2,它们具体是怎么创建的,怎么组合的,都交给了具体的产品product1,product2
3.更好的理解面向对象的原则,面向接口编程,而不是面向实现编程。
工厂模式适用于哪些场景呢
1.一个系统应当不依赖于产品类实例被创立,组成和表示的细节,就是说这个产品如何被创建,组成和表现,我们都归结到具体的产品是如何实现上去了,与前端的client,和中端的factory都是没有关系的
2.这个系统的产品至少有一个产品族,工厂方法模式就是一个产品族,它是最简单的一个等级
3.同属于同一个产品族的产品是设计成在一起使用的,这是毋庸置疑的,同属于一个系列的产品,就是在一起的
4.不同的产品以一系列的接口的面貌出现,从未使系统不依赖于接口实现的细节,要面向接口编程的,不是面向实现编程的