java设计模式(工厂模式)

代码说明(版本 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();
    }
}

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

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




  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值