工厂模式
也许有一些地方写的不够明确,如有错误,请各位大神指出。下面是从HeadFirst设计模式学习中,做的笔记:
一、简单工厂模式
传统的生成对象方式为new形式,如果一个接口有多个不同的实现类,根据不同的类型new不同的对象,常见的形式为if语句或switch语句来new对象。比如:推送微信消息,有一个抽象的实体类:SendMsg,其中有一个抽象方法为sendMsg()。给微信推送消息,可以推送模板消息,也可以推送图文消息。暂时有这两个子类。所以,类的基本形式如下:
public abstract class SendMsg {
public abstract void sendMsg();
//other method...
}
public class SendTempMsg extends SendMsg {
public void sendMsg(){
//发送模板消息代码
}
}
public class SendCustomerMsg extends SendMsg {
public void sendMsg(){
//推送图文消息代码
}
}
上面是对实现类的一个定义。如果要使用,传统方式如下:
public class Test {
public static void main(String[] args){
int type = 1;
SendMsg sendMsg;
switch (type) {
case: 1
sendMsg = new SendTempMsg();
break;
case: 2
sendMsg = new SendCustomerMsg();
break;
}
sendMsg.sendMsg();
}
}
简单工厂模式,基本是将new这一部分代码移动到了一个工厂类中。下面定义工厂类:
public class SendMsgFactory {
public static SendMsg createSendMsg(int type) {
SendMsg sendMsg;
switch (type) {
case: 1
sendMsg = new SendTempMsg();
case: 2
sendMsg = new SendCustomerMsg();
defaults:
throw new IllegalArguments("不支持创建其他类型");
}
return sendMsg;
}
}
使用类:
public class Test {
public static void main(String[] args) {
SendMsg sendMsg = SendMsgFactory.createSendMsg(1);
sendMsg.sendMsg();
}
}
上面形式便是简单工厂模式。好像简单工厂和传统方式并没有什么区别,只是把一部分代码移动到了一个单独的类中。但是,使用简单工厂方法模式以后,会发现,使用者完全不知道内部是如何实现的。只知道通过工厂可以创建一个推送消息的类。所以如果后期对子类进行增加或者删除时,使用者完全不知道。但是如果使用传统形式,使用者一看便知道,有两个推送消息的类,而且紧密的与这些类耦合在了一起,假如有一天不需要其中一个了,把它移除了,使用者也必须去把它移除。这样耦合度就比较高了。
二、工厂方法模式
定义:定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法的目的是将类的实例化推迟到子类。
大致类图如下:
温习工厂模式,是由于目前手上正好有一个任务,可以使用到工厂模式,所以借此机会,再熟悉熟悉工厂模式。
任务的背景大概是:
将称重商品的称重码导出为数据格式为指定格式的txt文件。目前的任务只支持导出一种格式,即DHTMA07格式。(格式为数据格式,不是导出后文件的格式,比如导出xml形式的txt文件,比如导出json形式的txt文件。为了方便,下面的例子以xml格式和json格式来举例。)
使用传统的方式实现:
public class PluFile {
public PluData buildJsonPluData(){
//...封装格式为json形式的数据
}
public PluData buildXmlPluData(){
//...封装格式为xml形式的数据
}
public void buildFile(PluData data){
//...将封装好的格式数据写入到一个本地文件中
}
}
public class PluFileTest {
public static void main(String[] args) {
PluFile pluFile = new PluFile();
PluData pluData = new PluData();
String type = "json";
switch (type) {
case "json":
pluData = pluFile.buildJsonPluData();
case "xml":
pluData = pluFile.buildXmlPluData();
default:
throw new IllegalArgumentsException("不支持的格式");
}
pluFile.buildFile(pluData);
}
}
上面是不适用工厂方法模式,测试类相当于客户端。PluFile直接暴露了其内部实现。这种方式是不容易扩展的。根据Effective Java中提到的,使类和成员的可访问性最小化,一旦类的成员暴露在外边,被多个地方调用,后期如有修改,将会变得很麻烦。而且客户端需要时时去关心它的内部实现。比如现在的新增了一个需求,导出格式为properties形式的txt文件。在服务端不仅要新增新建properties形式的,在客户端还要新增case语句。所以,这种方式虽然能实现功能,但并不是最好的实现方式。如果使用工厂方法模式:
/**
* 产品类。让具体的产品继承它,实现对应的格式
**/
public abstract class PluFile {
public abstract PluData buildData();
public void buildFile(PulData pluData) {
//将对应的格式写入到本地txt文件中
}
}
public class JsonPluFile extends PluFile{
public PluData buildData (){
//封装格式为json的格式
}
}
public class XmlPluFile extends PluFile {
public PluData buildData (){
//封装格式为xml的格式
}
}
/**
* 产品创建工厂。具体的产品,到具体的工厂类下创建,写成抽象类的原因是,可以将产品的一系列相同的操作放在一起。
**/
public abstract class PluFileFactory {
public abstract PluFile createPluFile();
public static PluFileFactory createFactoryByType(String type) {
switch (type) {
case "json":
return new JSONPluFileFactory();
case "xml":
return new XMLPluFileFactory();
default:
throw new IllegalArgumentException("不支持创建的格式");
}
}
public void createPluFileAndTotxt(List<ShopSku> shopSkuList) throws IOException, ShopSkuException {
PluFile pluFile = createPluFile();
pluFile.buildFile(pluFile.buildData());
}
}
/**
* 创建JSON形式的产品
**/
public class JsonPluFileFactory extends PluFileFactory {
public PluFile createPluFile (){
return new JsonPluFile();
}
}
/**
* 创建XML形式的产品
**/
public class XmlPluFileFactory extends XmlFileFactory {
public PluFile createPluFile (){
return new XmlPluFile();
}
}
public class PluFileTest {
public static void main(String[] args) {
String type = "json";
PluFileFactory.createFactoryByType(type).createPluFileAndTotxt();
}
}
使用工厂方法模式,它的好处是容易扩展。而且客户端并不知道其内部实现,如果后期扩展格式,客户端也不用关心具体如何实现,只要传入一个对应的格式,得到对应的结果即可。
三、抽象工厂模式