文章已收录到我的仓库:Java学习笔记及免费书籍分享模式类型工厂模式属于创建者模式,与对象的创建有关。工厂方法模式用于类,而抽象工厂模式用于对象。创建类模式将对象的部分创建推迟到创建对象的子类;创建对象模式将它推迟到另一个对象。模式设计意图工厂模式隐藏了复杂的对象创建工作,而只暴露一个接口供客户使用。具体的创建工作由工厂管理,由用户封装,将对象的创建和使用分离,降低了耦合度,易于管理,可以很好地支持变更。对于某些类,其对象的创建可能需要一系列复杂的参数或大量的准备代码。当这个任务由客户端完成时,如果客户端创建了多个复杂的对象,更何况这些代码可能很难写。会造成不必要的代码重复,但是交给工厂生成对象不会造成代码重复,因为工厂里的代码写一次就可以复用无数次,可以减少代码量,用户不用需要注意这些难写的代码,增加可读性,比如Spring框架中的beanFactory。另外,对象的创建和使用是分开的,所有的创建工作都由工厂管理。工厂可以在不暴露给客户的情况下管理类,适应相应的业务变化。比如某产品A升级为AA,我们可以直接在ChangereturnnewA()中返回工厂中的returnnewAA(),客户依然调用factory.createProductA(),无需更改代码。如果客户以newA()的形式创建了一个对象,那么我们需要找到所有的代码,并把它们改成newAA(),这在一个庞大的项目中是一项艰巨的任务。当客户使用的对象可能发生变化时,比如你一开始使用产品A,然后想使用产品B,我们可能要按照正常的方式更改所有新方法。如果使用简单工厂模式,只需要更改配置文件,而无需更改客户端代码即可达到此目的。如果使用工厂方法模式或者抽象工厂模式,可以通过调整工厂构造函数或者使用set注入来达到这个目的,大大减少变更带来的不兼容。以上都是由于类的使用和创建的解耦,这是整个工厂模式的核心思想和设计意图。在任何时候,高内聚和低耦合永远是写作时需要考虑的地方。工厂模式具体分为简单工厂、工厂方法和抽象工厂模式。这些方法都符合上述设计意图,但同时满足不同的需求,适应不同的场景。简单工厂设计定义了创建对象的接口,创建子类通过传入参数来决定创建哪个实例。适用场景工厂类负责生产较少的对象。当一个类不确定它必须创建的对象的类时,它就是不确定的。客户端知道需要传入工厂的参数,并不关心具体的类创建逻辑。代码示例假设我们有如下需求:某游戏厂商开发了A、B、C三款游戏,请测试人员试用对应的游戏。//定义游戏接口interfaceGame{publicvoidplay();}//A游戏类GameA实现Game{publicvoidplay(){System.out.println("PlayingGameA");};}//B游戏类GameBimplementsGame{publicvoidplay(){System.out.println("PlayingGameB");};}//C游戏类GameCimplementsGame{publicvoidplay(){System.out.println("PlayingGameC");};}classGameFactory{publicGamecreateGame(chartype){switch(type){case'A':returnnewGameA();案例“B”:返回新游戏B();case'C':返回新的}returnnull;}}//Tester(客户)类classTester{privatechartype;publicTester(chartype){this.type=type;}publicvoidtestGame(){GameFactorygameFactory=newGameFactory();//通过简单工厂获取游戏实例Gamegame=gameFactory.createGame(type);//尝试游戏game.play();}}//代码测试publicclassTest{publicstaticvoidmain(String[]args){//要求测试者试玩游戏ATestertestertester=newTester('A');tester.testGame();}}在测试代码Test类中,我们这样写:Testertester=newTester('A');其实我们在项目中并没有直接写类型,而是导入配置文件:Testertester=newTester(config.GAME_TYPE);这样,如果我们想让测试者测试不同的游戏时,可以修改config配置文件中的GAME_TYPE信息(也可以使用反射),这样可以让代码适应变化。如果每个类的构造准备都是一致的初始化(上面的代码没有任何准备工作直接返回),可以考虑使用哈希表来存储参数和实例之间的关系,这样可以减少大量的if-else语句。在这种情况下,我们需要在不考虑程序是否会被使用的情况下,提前将相关实例初始化并存储到哈希表中,这样可能会造成不必要的资源浪费,但另一方面,我们每次获取实例时都会返回HashMap实例克隆,比new操作更高效——这其实就是Flyweight设计模式。但是,在大多数情况下,我们仍然需要对不同的实例进行不同的初始化操作,此时仍然需要进行大量的判断语句。(如果类真的一致,使用了Map缓存,其实这个设计模式就是策略模式,策略模式是编程中最基本的设计模式,我们大部分人都在有意无意地使用它——定义算法接口。类实现不同的算法,并定义相关的类来选择这些子类。)UML类图总结简单工厂模式的结构非常简单易用,当对象实例较少时可以考虑简单工厂模式。通过UML类图可以发现,简单工厂模式封装了为客户创建具体类的逻辑过程,因此我们可以在工厂中进行一些复杂的初始化或其他操作,帮助客户减轻压力。用户只需要知道传入的具体参数即可,有对应的例子。简单工厂模式也可以在不影响客户端代码的情况下响应适度的业务变化,这符合工厂模式的设计意图。简单工厂模式的缺点也很明显。顾名思义,它只适用于比较简单的场景,稍微复杂一点的情况,简单工厂会非常庞大??,难以维护。在增减实例的时候,我们必须对工厂进行比较多的修改,这就违背了开闭原则。还有,当工厂有BUG的时候,整个程序就会崩溃。工厂方法(FactoryMethod)设计定义了创建对象的接口,由子类决定实例化哪个类。工厂方法将类的实例化推迟到子类实现。适用场景当一个类不确定它必须创建的对象是哪个类时,它就具有不确定性。您期望高可扩展性。当一个类希望它的子类指定它创建的对象时。当一个类将创建对象的责任委托给几个辅助子类之一时,客户端知道要使用哪个辅助子类。代码示例假设我们也有需求:某游戏厂商开发了A、B、C三款游戏,请测试人员试玩对应的游戏。//定义游戏接口interfaceGame{publicvoidplay();}//A游戏类GameA实现Game{publicvoidplay(){System.out.println("PlayingGameA");};}//B游戏类GameBimplementsGame{publicvoidplay(){System.out.println("PlayingGameB");};}//C游戏类GameCimplementsGame{publicvoidplay(){System.out.println("PlayingGameC");};}//定义工厂(父类)接口GameFactory{GamecreateGame();}//帮助子类,GameA工厂类GameAFactoryimplementsGameFactory{@OverridepublicGamecreateGame(){returnnewGameA();}}//帮助子类,游戏B工厂类GameBFactoryimplementsGameFactory{@OverridepublicGamecreateGame(){returnnewGameB();}}//帮助子类,游戏C工厂类GameCFactoryimplementsGameFactory{@OverridepublicGamecreateGame(){returnnewGameC();}}//Tester(客户)类classTester{privateGameFactorygameFactory;publicTester(GameFactorygameFactory){this.gameFactory=gameFactory;}publicvoidtestGame(){//通过工厂获取游戏实例Gamegame=gameFactory.createGame();//尝试游戏game.play();}}//代码测试publicclassTest{publicstaticvoidmain(String[]args){//测试者1需要尝试游戏AGameFactorygameFactory=newGameAFactory();测试员tester1=newTester(gameFactory);tester1.testGame();//要求测试员2试玩游戏ATestertestertester2=newTester(gameFactory);tester2.testGame();//...1000testeralsotrytoplaygameA}}我们可以通过更改构造函数或使用set方法来更改工厂,这样所有测试人员都可以用更少的代码更改玩游戏(在最坏的情况下,你仍然需要改很多代码),具有更好的灵活性UML类图总结工厂方法模式其实是简单工厂模式的扩展。工厂方法模式具有很高的可扩展性。如果你想在添加一个类的时候,直接写一个新的helper子类就可以了,不需要修改现有的代码就可以很方便的生产或者切换产品,完美的符合开闭原则。工厂方法模式是工程上比较理想的设计模式,但是它的缺点也很明显。当客户创建一个新产品时,他必须创建一个产品工厂,增加了代码量,并且很难修改父接口,因为一旦修改了接口,就必须修改很多helper子类。抽象工厂(AbstractFactory)设计提供了一个接口来创建一系列相关或相互依赖的对象,将类的实例化推迟到子类。适用场景代码需要与多个不同系列的相关产品进行交互,但由于无法提前获取相关信息,或者出于对未来扩展性的考虑,不希望代码基于产品的具体类构建但仅显示界面。需要创建的对象是一系列相互关联或相互依赖的产品系列。当一个系列中的产品设计为一起使用时,请在应用程序中仅使用同一系列中的对象。代码示例假设需求如下:某品牌的鞋垫可以生产大码鞋和对应的鞋垫,小码鞋和对应的鞋垫,现在客户想买一双鞋穿。//定义鞋子接口interfaceShoe{voidprintShone();}//定义鞋垫接口interfaceInsole{voidprintInsole();}//具体产品,大鞋类LargeShoesimplementsShoe{@OverridepublicvoidprintShone(){System.out.println("大鞋");}}//具体产品,大鞋垫类LargeInsoleimplementsInsole{@OverridepublicvoidprintInsole(){System.out.println("largeinsole");}}//具体产品小鞋类SmallShoesimplementsShoe{@OverridepublicvoidprintShone(){System.out.println("小鞋");}}//具体产品,小鞋垫classSmallInsoleimplementsInsole{@OverridepublicvoidprintInsole(){System.out.println("小鞋垫");}}//定义一个完整的鞋厂接口,一个完整的鞋子由鞋子和鞋垫接口CompleteShoeFactory{ShoecreateShoe();InsolecreateInsole();}//大型鞋厂,生产配套的大鞋和鞋垫}@OverridepublicInsolecreateInsole(){返回新的L鞋垫();}}//小型鞋厂,生产配套的小鞋和鞋垫}@OverridepublicInsolecreateInsole(){returnnewSmallInsole();}}//客户类,买鞋类Customer{privateCompleteShoeFactoryfactory;公共客户(CompleteShoeFactory工厂){this.factory=factory;}//购买完整的鞋子publicvoidbuyCompleteShoe(){ShoemyShoe=factory.createShoe();myShoe.printShone();鞋垫myInsole=factory.createInsole();我的鞋垫.printInsole();System.out.println("我买了配套的商品,终于有鞋了!");}}//代码测试类publicclassTest{publicstaticvoidmain(String[]args){//采购大鞋//这里一般使用单例模式生成工厂CompleteShoeFactoryfactory=newCompleteLargeShoeFactory();客户客户=新客户(工厂);customer.buyCompleteShoe();}}理解抽象工厂需要创建一系列对象相互关联或相互依存的产品族的好处,比如这里是大鞋配大鞋垫,小鞋配小鞋垫,这就是对应的产品族,消费者要么买大的产品,要么买小的产品,它不可能买到鞋垫小的大鞋,这使得特定的工厂类在一个应用中只出现一次,所以特定的工厂类通常使用单例设计模式来创建,因为特定的工厂类在一个应用中只出现一次,它更改产品系列会非常简单,我们只需要修改一行代码——创建工厂时的代码,具有很大的灵活性依赖产品系列,产品系列容易互换,更能应对变化。因为一个对象的产品被设计为协同工作,所以也有利于保持产品的一致性。如果采用简易工厂模式,那么必须创建四个工厂——一个大鞋厂、一个大鞋垫厂、一个小鞋厂和一个鞋垫厂。类的数量也增加了,用户可能会不小心创建大鞋子和小鞋垫,导致结果不合适。使用抽象工厂模式可以更好的解决这个问题。抽象工厂模式的缺点是当工厂接口的功能越来越多时,会越来越繁琐,因为所有的子类都必须实现这个接口,这就要求不同系列的产品类型保持一致另外,如果你以后要添加新类别或删除类别,您必须对所有子类别进行更改。结束语工厂模式包括简单工厂模式、工厂方法模式或抽象工厂模式。它们在形式和特性上都非常相似,最终目的都是为了解耦。在使用的时候,我们不必关心该模式是工厂方法模式还是抽象工厂模式,因为它们之间的演化过程往往令人困惑。经常你会发现,明明用的工厂方法模式,当有新的需求来了,稍微修改一下,增加一个新的方法,因为类中的产品构成了不同层次结构的产品族,就变成了抽象工厂模式为抽象工厂模式,当一个方法被缩减,使得提供的产品不再构成一个产品族时,就演变成工厂方法模式。因此,在使用工厂模式时,只需要看是否达到设计六大原则的目的即可。
