java设计模式(工厂模式)

来源:互联网 发布:数控编程招聘的视频 编辑:程序博客网 时间:2024/06/16 21:38

代码说明(版本 1)

比如苹果、橙子都是水果,那么可以定义一个水果工厂,工厂中有个方法叫做榨果汁,如果传入的水果为苹果,那么就榨出来苹果汁,如果是橙子那就榨出来橙汁。

按照之前我的写法是这样的

Fruit.java

public abstract class Fruit {    abstract void makeJuice();}

这个没什么好说的,我们假设水果可以榨自己成为果汁,所以给它弄一个 makeJuice 榨果汁 方法。

然后对应的两个实现类,分别榨出属于自己的果汁。

Apple.java

public class Apple extends Fruit {    @Override    void makeJuice() {        System.out.println("榨苹果汁了,快来喝");    }}

Orange.java

public class Orange extends Fruit {    @Override    void makeJuice() {        System.out.println("榨橙汁了,快来喝");    }}

当然,水果不是凭空得来的,得有对应的工厂去生产它,所以我们再写一个工厂类。

AbstractFactory.java

public abstract class AbstractFactory {    abstract Fruit create(String className);}

这里定义了一个抽象工厂,主要是因为,可能会有多个苹果工厂来生产苹果。

FruitFactory.java

public class FruitFactory extends AbstractFactory {    @Override    public Fruit create(String className) {        if ("apple".equals(className))            return new Apple();        if ("orange".equals(className))            return new Orange();        return null;    }}

水果工厂也定义好了,现在只需要客户端去调用就可以生产水果了。

FruitClient.java

public class FruitClient {    public static void main(String[] args) {        FruitFactory fruitFactory = new FruitFactory();        Fruit apple = fruitFactory.create("apple");        Fruit orange = fruitFactory.create("orange");        apple.makeJuice();        orange.makeJuice();    }}

执行我们的 main 方法,客户端依次打印出

榨苹果汁了,快来喝

榨橙汁了,快来喝

以上其实就是一个工厂模式。

问题:如果这时候新来了西瓜,需求说现在要榨西瓜汁了,该怎么办?

想都不用想,二话不说,我们新增一个西瓜类

Watermelon.java

public class Watermelon extends Fruit {    @Override    void makeJuice() {        System.out.println("榨西瓜汁了,要来喝吗?");    }}

然后修改我们的工厂类,改后的代码如下:

public class FruitFactory extends AbstractFactory {    @Override    public Fruit create(String className) {        if ("apple".equals(className))            return new Apple();        if ("orange".equals(className))            return new Orange();        if ("watermelon".equals(className))            return new Watermelon();        return null;    }}

最后修改我们的客户端,增加一个

Fruit watermelon = fruitFactory.create("watermelon");

这样我们就创建了一个西瓜,然后调用 watermelon.makeJuice() 就能榨西瓜汁了。

一切看起来都是那么完美,直到……

直到项目经理告诉你说,现在又新来了几种水果。

这时候就有问题了,我们发现每次一新来水果,我们的工厂类就要改变,这不正是违反了开闭原则吗?

开闭原则(对扩展开放,对修改关闭),简单来说,就是对于新的需求,尽量在原有的类上拓展,比如继承,然后重写原来的方法等,尽量不要在原有的类上进行修改,因为 bug 不仅是写出来的,也是改出来的。

并且既然都是工厂类了,我们肯定希望它能稳定点,总是变来变去,说不准哪天就倒闭了呢?

代码说明(版本 2)

版本 1 的代码既然违反了开闭原则,那么我们就来修复它。

可以看到,造成我们反复修改工厂类的主要原因是,每新来一种水果,我们就要将水果的名称传入,然后 new 对应的实例。

那么可不可以,不需要我们主动判断实例类型,不用显式地去 new 对应的实例呢?可不可以在程序运行的时候,能够自动获取到对应的实例呢?

答案是肯定的。这个时候就要有请开着上帝模式的反射登场了。

利用反射机制,我们可以获取到类的各种信息,比如类型,方法,参数,属性,还能修改访问权限等。

总之,反射好像做啥都行,我们这里要创造一个实例,很简单啊,反射给你做!

修改我们的代码:

AbstractFactory.java

public abstract class AbstractFactory {    abstract <T extends Fruit> T create(Class<T> c);}

注意,这里我们的参数由之前的 String 改成了 Class<T> ,主要就是实现反射时候需要用到类名。然后修改我们的具体实现类。

FruitFactory.java

public class FruitFactory extends AbstractFactory {    @Override    <T extends Fruit> T create(Class<T> c) {        try {            return (T) Class.forName(c.getName()).newInstance();        } catch (Exception e) {            e.printStackTrace();        }        return null;    }}

这样,在程序运行时,就会利用反射机制自动为我们创建对应的实例。

获取实例的几种方式

  1. Object.class 直接调用对象的 .class
  2. Class.forName(c.getName())
  3. 通过实例来获取

Apple apple = new Apple()apple.getClass()

  1. 通过类加载器获取

我们这里采用第二种方式获取实例。

最后修改我们的客户端:

FruitClient.java

public class FruitClient {    public static void main(String[] args) {        FruitFactory fruitFactory = new FruitFactory();        Fruit apple = fruitFactory.create(Apple.class);        apple.makeJuice();    }}

这样的话,如果新增一个水果,那么我们只需要传入对应的类型就可以了,也就不用频繁修改工厂类了。

好了,到此我们就实现了一个简单工厂。