关于设计模式:一文彻底搞懂工厂模式FactoryPattern

122次阅读

共计 6850 个字符,预计需要花费 18 分钟才能阅读完成。

文章已收录我的仓库:Java 学习笔记与收费书籍分享

模式类型

工厂模式属于创建者模式,与对象的创立无关,其中工厂办法模式用于类,而形象工厂模式用于对象。创立型类模式将对象的局部创立工作提早到子类,由子类创建对象;而创立型对象模式将它提早到另一个对象中。

模式设计用意

工厂模式将简单的对象创立工作暗藏起来,而仅仅暴露出一个接口供客户应用,具体的创立工作由工厂治理而对用户封装,将对象的创立和应用拆散开来,升高耦合度,便于管理,可能很好的反对变动。

对于某些类而言,其对象的创立可能是须要一系列简单的参数或大量筹备代码,当这个工作由客户实现时,如果客户创立多个简单对象,先不说这些代码可能难以编写,也会造成不必要的代码反复,交给工厂产生对象则不会造成代码反复,因为工厂中的代码写一次便能够复用无数次,可能缩小代码量,并且用户无需关注这些难以编写的代码,减少可读性,例如 Spring 框架中的 beanFactory 便是如此。

此外,将对象的创立和应用拆散开来,所有的创立工作由工厂治理,工厂能够治理类并适应相应业务变动,而无须对客户裸露,例如如果某个产品 A 降级为 AA,咱们能够间接在工厂中将 return new A() 改为 return new AA(),客户依然调用factory.createProductA() 而无须变更代码,如果客户是通过 new A() 的模式创建对象,那么咱们还须要找到所有的代码并把它们改成new AA(),这在一个宏大的工程中是一项微小的工作。

当客户应用的对象可能发生变化时,例如一开始应用 A 产品而起初想应用 B 产品时,一般的形式咱们可能不得不更改所有的 new 办法,如果应用简略工厂模式,能够仅仅更改配置文件而无须更改客户代码来实现这个目标,如果应用工厂办法模式或者形象工厂模式,那么能够调整工厂的构造函数或者应用 set 注入来达到这个目标,大大减少因变动而造成的不适应性。

上述所说的所有都归功于将类的应用与创立解耦,这也就是整个工厂模式的核心思想与设计用意。无论何时,高内聚、低耦合永远都是编写须要思考到的中央。

工厂模式具体分为简略工厂、工厂办法和形象工厂模式,这些办法都合乎上述设计用意,但同时又满足不同的需要、适应不同的场景。

简略工厂(Simple Factory)

设计

定义一个创建对象的接口,创立一个子类通过传入的参数决定创立哪一种实例。

实用场景

  • 工厂类负责生产的对象较少。
  • 一个类不确定它所必须创立的对象的类的时候,具备不确定性。
  • 客户晓得须要传入工厂的参数而并不关怀具体的类创立逻辑。

代码实例

假如咱们具备如下需要:一家游戏厂商开发了 A、B、C 三种游戏,某个测试者被要求试玩相应游戏。

// 定义游戏接口
interface Game {public void play();
}

// A 游戏
class GameA implements Game{public void play(){System.out.println("Playing GameA");};
}

// B 游戏
class GameB implements Game{public void play(){System.out.println("Playing GameB");};
}

// C 游戏
class GameC implements Game{public void play(){System.out.println("Playing GameC");};
}

class GameFactory {public Game createGame(char type) {switch (type) {
            case 'A':
                return new GameA();

            case 'B':
                return new GameB();

            case 'C':
                return new GameC();}
        return null;
    }
}

// 测试者 (客户) 类
class Tester {
    private char type;
    public Tester(char type) {this.type = type;}
    public void testGame() {GameFactory gameFactory = new GameFactory();
        // 通过简略工厂获取游戏实例
        Game game = gameFactory.createGame(type);
        // 试玩游戏
        game.play();}

}

// 代码测试
public class Test {public static void main(String[] args) {
        // 要求测试者试玩游戏 A
        Tester tester = new Tester('A');
        tester.testGame();}
}

在测试代码 Test 类中咱们是这样写的:

Tester tester = new Tester('A');

事实上在工程中咱们并不间接写类型,而是导入配置文件:

Tester tester = new Tester(config.GAME_TYPE);

这样如果咱们想要让测试者测试不同的游戏时,就能够批改 config 配置文件中的 GAME_TYPE 信息(还能够利用反射),这就使得代码可能适应变动。

如果每个类的结构筹备工作都是统一的初始化(上述代码没有任何筹备工作而间接返回),能够思考采纳哈希表存储参数与实例之间的关系,能够缩小大量 if-else 语句。在这种状况下咱们须要提前在哈希表中初始化并存储相干实例而没有思考程序是否会用上,兴许会造成不必要的资源节约,但另一方面,咱们每次获取实例都是返回 HashMap 中实例的克隆,这样比 new 操作效率更高——这其实就是享元设计模式。不过,大多数状况下咱们仍须要对不同的实例进行不同的初始化操作,此时仍须要大量的判断语句。(如果类真的具备一致性而采纳 Map 缓存,事实上这种设计模式就是策略模式,策略模式是编程中最根本的设计模式,咱们大多都在有意无意的应用——定义算法接口并由子类实现不同算法,定义相干类以抉择这些子类。)

UML 类图

总结

简略工厂模式的构造非常简单,易于应用,当对象实例较少时能够思考简略工厂模式。通过 UML 类图能够发现,简略工厂模式对客户封装了具体创立类的逻辑过程,所以咱们能够在工厂中进行一些简单的初始化或其余操作帮忙客户加重压力,用户仅仅须要晓得传入的具体参数即可取得相应实例。简略工厂模式也能够应答适量的业务变动而不影响客户代码,合乎工厂模式的设计用意。

简略工厂模式的毛病也是很显著的,正如它的名字,它仅仅实用于较为简单的场景,而对稍加简单的状况会使得简略工厂无比宏大而难以保护,当减少或缩小实例时,咱们必须对工厂进行较为大量的批改,这违反了开闭准则。此外,当工厂呈现 BUG 时,整个程序将会解体。

工厂办法(Factory Method)

设计

定义一个创建对象的接口,由子类决定实例化哪一个类,工厂办法将类的实例化推延到子类实现。

实用场景

  • 一个类不确定它所必须创立的对象的类的时候,具备不确定性。
  • 你冀望取得较高的扩展性。
  • 当一个类心愿由它的子类来指定它所创立的对象。
  • 当类将创建对象的职责委托给多个帮忙子类的中的某一个,并且客户晓得将要应用哪一个帮忙子类。

代码实例

假如咱们同样具备需要:一家游戏厂商开发了 A、B、C 三种游戏,测试者被要求试玩相应游戏。

// 定义游戏接口
interface Game {public void play();
}

// A 游戏
class GameA implements Game{public void play(){System.out.println("Playing GameA");};
}

// B 游戏
class GameB implements Game{public void play(){System.out.println("Playing GameB");};
}

// C 游戏
class GameC implements Game{public void play(){System.out.println("Playing GameC");};
}

// 定义工厂(父类)
interface GameFactory {Game createGame();
}

// 帮忙子类,游戏 A 工厂
class GameAFactory implements GameFactory {
    @Override
    public Game createGame() {return new GameA();
    }
}

// 帮忙子类,游戏 B 工厂
class GameBFactory implements GameFactory {
    @Override
    public Game createGame() {return new GameB();
    }
}

// 帮忙子类,游戏 C 工厂
class GameCFactory implements GameFactory {
    @Override
    public Game createGame() {return new GameC();
    }
}

// 测试者 (客户) 类
class Tester {
    private GameFactory gameFactory;

    public Tester(GameFactory gameFactory) {this.gameFactory = gameFactory;}

    public void testGame() {
        // 通过工厂获取游戏实例
        Game game = gameFactory.createGame();
        // 试玩游戏
        game.play();}

}

// 代码测试
public class Test {public static void main(String[] args) {
        // 要求测试者 1 试玩游戏 A
        GameFactory gameFactory = new GameAFactory();
        Tester tester1 = new Tester(gameFactory);
        tester1.testGame();
        
         // 要求测试者 2 也试玩游戏 A
        Tester tester2 = new Tester(gameFactory);
        tester2.testGame();
        
        //... 测试者 1000 也试玩游戏 A
    }
}

咱们能够通过更改构造函数或应用 set 办法来更改工厂,能够更改较少的代码就能让所有测试者都更改试玩游戏(最坏状况下依然须要更改较多代码),具备较好的灵活性。

UML 类图

总结

工厂办法模式其实就是简略工厂模式一种扩大,工厂办法模式具备高扩展性,如果后续想要减少类时,间接编写一个新的帮忙子类即可,能够很不便的生产或切换产品,而无需批改现有的代码,完满合乎了开闭准则·。

工厂办法模式是工程中较为理想的一种设计模式,但它的毛病也是显著的,当客户新建一个产品时,就不得不创立一个产品工厂,减少了代码量,而且对于父类接口难以进行批改,因为一旦批改接口,就必须要对泛滥的帮忙子类进行批改。

形象工厂(Abstract Factory)

设计

提供一个接口以创立一系列相干或相互依赖的对象,将类的实例化提早到子类。

实用场景

  • 代码须要与多个不同系列的相干产品交互,然而因为无奈提前获取相干信息,或者出于对将来扩展性的思考,你不心愿代码基于产品的具体类进行构建而仅心愿显示创立它们的接口。
  • 须要创立的对象是一系列互相关联或相互依赖的产品族。
  • 当一系列中的产品被设计为一起应用时,在一个利用中仅应用同一个系列中的对象。

代码实例

假如具备需要如下:一家品牌鞋垫能够生产大号尺寸的鞋子和对应鞋垫和小号尺寸的鞋子和对应鞋垫,当初有一个客户想要购买一双鞋子穿。

// 定义鞋子接口
interface Shoe {void printShone();
}

// 定义鞋垫接口
interface Insole {void printInsole();
}

// 具体产品,大号鞋子
class LargeShoes implements Shoe {
    @Override
    public void printShone() {System.out.println("大号鞋子");
    }
}

// 具体产品,大号鞋垫
class LargeInsole implements Insole {
    @Override
    public void printInsole() {System.out.println("大号鞋垫");
    }
}

// 具体产品,小号鞋子
class SmallShoes implements Shoe {
    @Override
    public void printShone() {System.out.println("小号鞋子");
    }
}

// 具体产品,小号鞋垫
class SmallInsole implements Insole {
    @Override
    public void printInsole() {System.out.println("小号鞋垫");
    }
}

// 定义残缺鞋子工厂接口,一个残缺的鞋子由鞋子和鞋垫组成
interface CompleteShoeFactory {Shoe createShoe();
    Insole createInsole();}

// 大型鞋子工厂,生产配套的大号鞋子和鞋垫
class CompleteLargeShoeFactory implements CompleteShoeFactory {
    @Override
    public Shoe createShoe() {return new LargeShoes();
    }

    @Override
    public Insole createInsole() {return new LargeInsole();
    }
}

// 小号鞋子工厂,生产配套的小号鞋子和鞋垫
class CompleteSmallShoeFactory implements CompleteShoeFactory {
    @Override
    public Shoe createShoe() {return new SmallShoes();
    }

    @Override
    public Insole createInsole() {return new SmallInsole();
    }
}

// 客户类,购买鞋子
class Customer {
    private CompleteShoeFactory factory;

    public Customer(CompleteShoeFactory factory) {this.factory = factory;}

    // 购买残缺的鞋
    public void buyCompleteShoe() {Shoe myShoe = factory.createShoe();
        myShoe.printShone();

        Insole myInsole = factory.createInsole();
        myInsole.printInsole();

        System.out.println("我曾经买了配套产品,终于有鞋穿了!");
    }
}

// 代码测试类
public class Test {public static void main(String[] args) {
        // 购买大号鞋子
        // 这里通常应用单例模式生成工厂
        CompleteShoeFactory factory = new CompleteLargeShoeFactory();
        Customer customer = new Customer(factory);
        customer.buyCompleteShoe();}
}

了解形象工厂须要创立的对象是一系列互相关联或相互依赖的产品族的益处,例如这里的大号鞋子配大号鞋垫,小号鞋子配小号鞋垫,这就是对应的产品族,消费者要么买大号产品,要么买小号产品,不可能买大号鞋配小号鞋垫,这就使得一个具体工厂类在一个利用中仅呈现一次,因而具体工厂类通常应用单例设计模式创立,因为一个具体工厂类在一个利用中仅呈现一次,那么变更产品系列将非常简略,咱们仅须要批改一行代码——创立工厂时的代码,这具备极大的灵活性。

UML 类图

总结

形象工厂模式用于创立的对象是一系列互相关联或相互依赖的产品族,易于替换产品系列,可能较好的应答变动。因为一个对象的产品被设计成一起工作,因而也利于保护产品的一致性,如果采纳简略工厂模式,那么得创立四个工厂——大鞋子工厂、大鞋垫工厂、小鞋子工厂和鞋垫工厂,引入的类也增多,而且用户可能会不小心创立大鞋子和小鞋垫,使得后果不适配,采纳形象工厂模式则能够较好的解决这个问题。

形象工厂模式的毛病在于当工厂接口的性能越来越多时,它会变得越来越轻便,因为所有的子类都必须要去实现这里接口,这就要保障不同系列的产品种类都是统一的,此外,后续想要减少新品种或者删减品种,都不得不对所有子类做出更改。

结语

工厂模式包含简略工厂模式,工厂办法模式,还是形象工厂模式,它们在模式和特点上是极为类似的,而且最终目标都是为理解耦。在应用时,咱们不用去在意这个模式到底工厂办法模式还是形象工厂模式,因为他们之间的演变经常是令人琢磨不透的。常常你会发现,明明应用的工厂办法模式,当新需要降临,稍加批改,退出了一个新办法后,因为类中的产品形成了不同等级构造中的产品族,它就变成形象工厂模式了;而对于形象工厂模式,当缩小一个办法使的提供的产品不再形成产品族之后,它就演变成了工厂办法模式。

所以,在应用工厂模式时,只须要设计的六大准则的目标是否达到了。

正文完
 0