设计模式第二弹,工厂方法模式
简单工厂模式虽然简单,但是存在一个很严重的问题,那就是当系统要引入新的产品的时候,由于静态工厂的方法是通过入参来选择创建不同的产品,所以必然要修改工厂类的源代码,违反开闭原则,那么如何才能实现新增产品而不影响已有的代码?工厂方法模式应运而生。
在简单工厂模式中,只有一个工厂类,那么所有的对象创建细节它都必须知道,并决定何时实例化一个产品,工厂模式最大的确定就是当有新产品要加入系统时,必须修改工厂类,需要再其中加入必要的业务逻辑,违反开闭原则,此外,具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和拓展性。
在工厂方法模式中不再提供一个统一的工厂类来创建所有的产品对象,二是针对不用的产品提供不同的工厂,系统提供一个与产品等级结构相对应的共产等级结构。
工厂方法模式:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类,工厂方法模式又简称为工厂模式。
思考:工厂方法模式中的工厂方法能否为静态方法?为什么?
答:不能,因为需要子类去各自实现,抽象方法由子类动态实现,而动态性与static所声明的静态性冲突。
思考:有人说:可以在客户端代码中直接使用反射机制来生成产品对象,在定义产品对象时使用抽象类型,同样可以确保系统的灵活性和可拓展性,增加新的具体产品无须修改源代码,只需要将其作为抽象产品类的子类,根本不需要抽象工厂类和具体工厂类。试思考这种做法是否可行,如果可行,这种做法是否存在问题,为什么?
答:工厂的作用除了将对象的创建和对象的使用分离之外,还有一个作用就是可以屏蔽一些对象初始化的工作,如果直接反射得到具体产品对象,那么反射之后,需要每次做初始化工作,而工厂中就直接初始化完成了。反射生成对象只能适用一些最简单的情况,如果对象的创建比较复杂,例如要调用有参的构造函数,创建之前需要配置相应的环境,则需要将这些代码封装到工厂中,而工厂的作用不仅仅是简单的创建一个对象。
练习:使用工厂方法模式设计一个程序来读取各种不同类型的图片格式,针对每一种图片格式都设计一个图片读取器,如GIF图片读取器用于读取GIF格式的图片、JPG图片读取器用于读取JPG格式的图片。需充分考虑系统的灵活性和可扩展性。
public interface ReadFactory {
ReadPicTool creatPicTool();
ReadPicTool creatPicTool(byte[] bytes);
void read();
}
class GIFReadFactory implements ReadFactory{
public ReadPicTool creatPicTool() {
return new GifPicRead();
}
public ReadPicTool creatPicTool(byte[] bytes) {
return new GifPicRead();
}
public void read() {
GifPicRead read= (GifPicRead) creatPicTool();
read.read();
}
}
class PngReadFactory implements ReadFactory{
public ReadPicTool creatPicTool() {
return new PngPicRead();
}
public ReadPicTool creatPicTool(byte[] bytes) {
return new PngPicRead();
}
public void read() {
PngPicRead read= (PngPicRead) creatPicTool();
read.read();
}
}
public interface ReadPicTool {
void read();
}
class GifPicRead implements ReadPicTool{
public void read() {
System.out.println("读取GIF图片");
}
}
class JpgPicRead implements ReadPicTool{
public void read() {
System.out.println("读取JPG图片");
}
}
class PngPicRead implements ReadPicTool{
public void read() {
System.out.println("读取PNG图片");
}
}
/**
* 解析器
*/
public class XMLUtil {
public static Object getReadFactory() {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc;
doc=builder.parse(new File("config.xml"));
NodeList nl=doc.getElementsByTagName("factory");
Node classNode=nl.item(0).getFirstChild();
String readFactory=classNode.getNodeValue().trim();
Class c=Class.forName(readFactory);
Object obj=c.newInstance();
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
public static void main(String[]args){
GIFReadFactory factory=new GIFReadFactory();
GifPicRead read = (GifPicRead) factory.creatPicTool();
read.read();
factory.read();
JpgReadFactory readFactory = (JpgReadFactory) XMLUtil.getReadFactory();
JpgPicRead readPicTool = (JpgPicRead) readFactory.creatPicTool();
readPicTool.read();
readFactory.read();
}
跟之前的逻辑差不多,也都是简单的应用。