概念:工厂模式根据实现的方式,简单工厂模式、工厂方法模式、抽象工厂模式,他们是层层递进的,实现的复杂性和应用场景都是不同的。
下面对不同的设计模式一 一进行学习和记录。
1. 简单工厂模式
简单工厂模式是属于创建型模式,又叫做静态工厂方法(static Factory Method)模式,简单工厂模式是由一个工厂对象决定创建出来哪一种产品类的实例.
简单的说创建一个工厂类,工厂类有一个静态的方法,方法根据不同的参数标识,创建不同的具体类(有同属的父类),然后供外部调用实现不同的功能。
直接来个程序实例吧,举一个买票的例子,有三种票"汽车票",“飞机票”,“火车票”。先创建一个票的父类接口 Ticket.
interface Ticket{
void saleTicket();
}
再创建不同的具体的售票类
// 汽车票
public class BusTicket implements Ticket{
@Override
public void saleTicket() {
System.out.print("出售汽车票");
}
}
//飞机票
public class AirPlaneTicket implements Ticket{
@Override
public void saleTicket() {
System.out.print("出售飞机票");
}
}
// 火车票
public class TrainTicket implements Ticket{
@Override
public void saleTicket() {
System.out.print("出售火车票");
}
}
然后创建一个工厂类,通过静态方法创建返回不同的售票类
class FactoryCreater {
public static Ticket saleDiffrenetTicket(String type) {
Ticket ticket= null;
if ("bus".equalsIgnoreCase(type)) {
ticket = new BusTicket ();
}
else if ("airplane".equalsIgnoreCase(type)) {
ticket = new AirPlaneTicket ();
}
else if ("train".equalsIgnoreCase(type)) {
ticket= new TrainTicket ();
}
return ticket;
}
}
这样就根据传入的不同的参数,完成了对应类的创建,拿到不同的类进行不同的业务处理:
Ticket busTicket = FactoryCreate.saleDiffrenetTicket("bus");
busTicket.saleTicket(); // 出售汽车票
Ticket airplaneTicket = FactoryCreate.saleDiffrenetTicket("airplane");
airplaneTicket.saleTicket(); //出售飞机票
Ticket trainTicket = FactoryCreate.saleDiffrenetTicket("train");
trainTicket.saleTicket(); //出售火车票
就这样一个简单工厂模式完成,其实很简单,就是创建类,他有类似工厂的作用的,根据投入的不同材料生产出不同的成品供使用,这个生产的过程是静态的。他的优缺点也相对比较明显:
优点:
对类的创建进行了封装,客户端可以免除直接创建对象的职责,只关心使用对象,简单工厂模式实现了对象创建和使用的分离。
缺点:
系统扩展困难,一旦添加新产品就不得不修改工厂逻辑。
简单工厂模式由于使用了静态工厂方法,所以工厂角色无法形成基于继承的等级结构
2.工厂方法模式
既然简单工厂存在一些问题,那就要解决问题,毕竟程序员就是面对问题,解决问题的呀。静态工厂类让代码失去了扩展性,那怎么解决这个新增不同的车票类,例如新加一个售卖船票的类,不用修改已有工厂类,不违背开闭原则呢。
下面工厂方法类出场了:
工厂方法模式就不再像简单工厂一样对所有的票务系统集成到一个工厂里面进行实例化,而是针对具体不同的票给出独立的工厂类。
直接代码见吧。几个售票的具体类还是和上面一样,工厂方法模式和简单模式有差的地方如下:
- 多了一个工厂接口类`
//创建技能工厂模块
interface IFactory {
Ticket saleDiffrenetTicket();
}
具体的售出不同的票务的类就去实现这个工厂类,创建不同的票务产品
class BusTicketFactory implements IFactory {
@Override
public Ticket saleDiffrenetTicket() {
return new BusTicket ();
}
}
class AirPlaneFactory implements IFactory {
@Override
public Ticket saleDiffrenetTicket() {
return new AirPlaneTicket ();
}
}
class TrainFactory implements IFactory {
@Override
public Ticket saleDiffrenetTicket() {
return new TrainTicket ();
}
}
现在就需要什么票就创建不同的票类工厂, 新增票类的话,就新增具体的产品和工厂,不会去改动之前的写好的类,进行了解耦,但这样确实带来了更多的类和代码。
IFactory factory = new BusTicketFactory ();
Ticket busTicket = factory.saleDiffrenetTicket();
busTicket .saleTicket();
IFactory factory = new AirPlaneFactory ();
Ticket airPlaneticket = factory.saleDiffrenetTicket();
airPlaneticket .saleTicket();
同样总结下主要的优缺点:
优点:
在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,完全符合“开闭原则”。
缺点:
1.在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
2.由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。
备注:我一开始在想就创建一个类为什么要搞那么复杂呢,还不如直接new 一个对象来的方便,有效。经过思考学习,是我的认识太肤浅了,我们学习的时候学到的例子都是很简单的事例,创建一个类的时候都没有做什么初始化。
以下是学习的时候看到网上的总结,说的蛮清晰的:
1.工厂模式是为了解耦:把对象的创建和使用的过程分开。就是Class A 想调用 Class B ,那么A只是调用B的方法,而至于B的实例化,就交给工厂类。
2.工厂模式可以降低代码重复。如果创建对象B的过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。我们可以这些创建对象B的代码放到工厂里统一管理。既减少了重复代码,也方便以后对B的创建过程的修改维护。(当然,我个人觉得也可以把这些创建过程的代码放到类的构造函数里,同样可以降低重复率,而且构造函数本身的作用也是初始化对象。不过,这样也会导致构造函数过于复杂,做的事太多,不符合java 的设计原则。)
3.由于创建过程都由工厂统一管理,所以发生业务逻辑变化,不需要找到所有需要创建B的地方去逐个修正,只需要在工厂里修改即可,降低维护成本。同理,想把所有调用B的地方改成B的子类B1,只需要在对应生产B的工厂中或者工厂的方法中修改其生产的对象为B1即可,而不需要找到所有的new B()改为new B1()。
4.因为工厂管理了对象的创建逻辑,使用者并不需要知道具体的创建过程,只管使用即可,减少了使用者因为创建逻辑导致的错误。
贴一个讲的很详细的文章地址:https://blog.csdn.net/lovelion/article/details/7523392
存在的就是合理的,只是你还没有参透,如果只是为了炒概念装逼是上升不到这样的高度的。
3.抽象工厂模式
由于工厂方法模式中的每个工厂只生产一类产品(通过实现同一个工厂接口),带来的问题就是系统中会增加大量的工厂类。
有些系统的初始化会更复杂,不像上面这样只有一个一类型的产品,通过工厂模式单独去创建这一类产品。这个时候上面的简单工厂和工厂方法都能适用,但如果遇到有多个产品品类可能存在相互依赖的情况的话就会出现大量的工厂类,这个时候就需要把一些有依赖的类进行组合,有些人称之为产品族。
对上面的例子进行扩展:
上面的车票是出行的一个重要部分,另外一个重要的事情就是酒店,我们对酒店也和出行类型一样进行抽象分类,酒店有不同的类型,有廉价、精品、高档三个品类。
现在给出的结果是,根据用户的需要给出不同类型票的不同的等级的组合。首先需要抽象出一个的工厂类接口。
interface Hotel{
void selectHotel();
}
根据档次创建三种品类的酒店:
// 廉价酒店
public class CheapHotelt implements Hotel{
@Override
public void selectHotel() {
System.out.print("预定廉价酒店");
}
}
//精品酒店
public class BoutiqueHotel implements Hotel{
@Override
public void selectHotel() {
System.out.print("预定廉价酒店");
}
}
// 高级酒店
public class TopHotelt implements Hotel{
@Override
public void selectHotel() {
System.out.print("预定廉价酒店");
}
}
下面就是需要提供用户选择不同的出行交通方式和不同的档次的酒店。这两个产品在一趟旅行中是需要事先一起安排好的。这样的话就抽象出一个类去同时构建两种不同的产品类。进行一个产品组合。
interface IFactory {
Ticket saleDiffrenetTicket();
Hotel selectOneHotel();
}
根据上面的抽象接口就可以根据具体的用户选择构建不同的产品组合了。
比如穷游省钱,那就选择便宜的火车和廉价酒店:
QiongYou implements IFactory { // 穷游方案
@Override
public Ticket saleDiffrenetTicket() { // 买好火车票
return new TrainTicket ();
}
@Override
public Hotel selectOneHotel() {
return new CheapHotelt ();
}
}
这样就生成了一个穷游的方案,这样就能根据不同的需求创建出一些不同的组合方案给用户选择,同时不需要单独提供不同的工厂类,而是直接进行了产品的组合。这样的抽象工厂方式也还是一样的存在优劣势,总结一下主要的优缺点:
优点:不需要为每个产品类型的不同品类单独去创建一个工厂来实例化对象,可以对有关联的产品类型进行组合,实现一些定制化的方案需求。封装了一些细节,对代码结构进行了很好的梳理。
缺点:不同的产品组合情况多的话,代码的灵活性有一定的限制,修改组合和类型的话还是会比较麻烦,有一定的耦合。
注意:以上的三种工厂模式各有各的优劣,需要根据实际的项目需要进行合理的选择。甚至是搭配使用。