JavaSEJava 杂谈

一个简单的小例子理解工厂模式

2018-05-08  本文已影响3人  熊猫读书营

工厂模式应该是最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式,
下面用一个最简单的小例子解释一下什么是工厂模式。

编写例子

来模拟一个最简单的植物大战僵尸的小游戏。
假设初始有三种植物,绿豆、冰豆、坚果墙。
新建三个类如下:

public class Bean{
    public String getName(){
        return "豆子";
    }
    public void fight(){
        System.out.println("发射了一颗豆子");
    }
}
public class IceBean {
    public String getName(){
        return "冰豆";
    }
    public void fight(){
        System.out.println("发射了一颗冰豆");
    }
}
public class Wall {
    public String getName(){
        return "坚果墙。";
    }
    public void fight(){
        System.out.println("稳稳地立在那");
    }
}

接下来就定义简单游戏的功能方法:

public class Function {
    public void put(String name, int number){
        if (name.equals("Bean")){
            Bean bean = new Bean();
            System.out.println("将"+bean.getName()+"放在了"+number+"号草地上");
            bean.fight();
        }else if (name.equals("IceBean")){
            IceBean iceBean = new IceBean();
            System.out.println("将"+iceBean.getName()+"放在了"+number+"号草地上");
            iceBean.fight();
        }else if (name.equals("Wall")){
            Wall wall = new Wall();
            System.out.println("将"+wall.getName()+"放在了"+number+"号草地上");
            wall.fight();
        }
    }
}

Main函数测试:

public class Main {

    public static void main(String[] args) {
        Function test = new Function();
        test.put("Bean",3);
        test.put("Wall",5);
        test.put("IceBean",9);
    }
}

这样就实现了一个最最简单的例子。

第一个问题

上面的功能方法中我们明显可以发现一个问题,就是有很多类似的代码,我们是否可以想办法简化它?
可以想到能定义一个接口,在接口中定义要实现的方法,各个类去实现该接口。这样就可以将公用的部分提取出来了。
Plant接口如下:

public interface Plant {
    public String getName();
    public void fight();
}

修改功能方法:

public class Function {
    public void put(String name, int number){
        Plant plant = null;
        if (name.equals("Bean")){
            plant = new Bean();
        }else if (name.equals("IceBean")){
            plant = new IceBean();
        }else if (name.equals("Wall")){
            plant = new Wall();
        }
        System.out.println("将"+plant.getName()+"放在了"+number+"号草地上");
        plant.fight();
    }
}

这样就很好地简化了代码。

第二个问题

但是上面那样的修改又引来了第二个问题。就是当我们要修改和增加功能方法的内容时会很麻烦,比如我们要增加新功能,那么我们在用到创建类的每个地方都要去修改,工作量会很大。

所以我们要解决这种问题。想到可以用封装的方式,将对象创建的过程封装起来,需要用的时候直接去调用它就可以了。
新建一个简单工厂类:

public class SimpleFactory {
    public static Plant createPlant(String name){
        Plant plant = null;
        if (name.equals("Bean")){
            plant = new Bean();
        }else if (name.equals("IceBean")){
            plant = new IceBean();
        }else if (name.equals("Wall")){
            plant = new Wall();
        }
        return plant;
    }
}

修改功能方法:

public class Function {
    public void put(String name, int number){
        Plant plant = SimpleFactory.createPlant(name);
        System.out.println("将"+plant.getName()+"放在了"+number+"号草地上");
        plant.fight();
    }
}

这样修改后,当我们要增加新功能时只需要修改这一处就可以了。当我们用到某一类对象时,不需用去关心其创建过程,拿来直接用就好,对象的扩展与维护也都是单独去管理,这就是最简单的工厂模式。

开发中可能遇到的扩展问题

上面实现的简单工厂模式只能满足最基本的需求,在实际开发中可能会遇到很多扩展的需求,比如我们要增加一些其他的组件或者要在各个植物类中增加一些具体的属性,如豆子植物有头发、武器等,坚果墙有果壳等组件。
例:
增加头发类Hair、武器类Arms、果壳类Shell

public class Hair {
    private String color;
    
    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
public class Arms {
    private String bulletType; //子弹类型

    public String getBulletType() {
        return bulletType;
    }
    public void setBulletType(String bulletType){
        this.bulletType = bulletType;
    }
}
public class Shell {
    private Integer hardness; //果壳硬度

    public Integer getHardness() {
        return hardness;
    }

    public void setHardness(Integer hardness) {
        this.hardness = hardness;
    }
}

在植物类中添加需要的属性,如Bean类和IceBean类中要有hair、arms属性,Wall类中要有shell属性

private Hair hair;
    private Arms arms;

    public Hair getHair() {
        return hair;
    }

    public Arms getArms() {
        return arms;
    }

    public void setHair(Hair hair) {
        this.hair = hair;
    }

    public void setArms(Arms arms) {
        this.arms = arms;
    }

另两个类同理,可自行修改。

此时增加了一些属性组件后,SimpleFactory类中就不能直接用接口来创建对象了,而是需要更详细的创建过程。
修改SimpleFactory类如下:

public class SimpleFactory {
    public static Plant createPlant(String name){
        Plant plant = null;
        if (name.equals("Bean")){
            Bean bean = new Bean();
            Hair hair = new Hair();
            hair.setColor("绿色");
            bean.setHair(hair);
            Arms arms = new Arms();
            arms.setBulletType("普通豆子");
            bean.setArms(arms);
            plant = bean;
        }else if (name.equals("IceBean")){
            Bean bean = new Bean();
            Hair hair = new Hair();
            hair.setColor("蓝色");
            bean.setHair(hair);
            Arms arms = new Arms();
            arms.setBulletType("冰冻豆子");
            bean.setArms(arms);
            plant = bean;
        }else if (name.equals("Wall")){
            Wall wall = new Wall();
            Shell shell = new Shell();
            shell.setHardness(5);
            wall.setShell(shell);
            plant = wall;
        }

        return plant;
    }
}

做完上述修改后就可以满足我们新的需求。
但是,这样又引来了一个新的问题,当我们要新增植物时又会很麻烦,这样的代码不便于复用,而且可维护性很差。

解决方案:开闭原则

所谓开闭原则,就是对扩展开放,对修改关闭。
我们先来想想,设计模式的出现是为了什么?

设计模式的目的就是要解决代码复用的问题,增加代码的可维护性。
所以我们要对前面的代码再做修改。
首先方法中的判断肯定是不能要了,否则每种对象的创建过程一定是会混在一起的,这样就会产生一系列问题。

我们可以将每个类构造成独立的工厂,如BeanFactort类、IceFactory类、WallFactory类。这样当我们要修改时只需要修改对应的工厂类即可,要增加新的植物时也只需要新增一个工厂类,不必去修改本来的代码,实现了一定的解耦。

代码实现:工厂方法

按照上面的原则,新建BeanFactory类:

public class BeanFactory {
    public Plant createPlant(){
        Bean bean = new Bean();
        Hair hair = new Hair();
        hair.setColor("绿色");
        bean.setHair(hair);
        Arms arms = new Arms();
        arms.setBulletType("普通豆子");
        bean.setArms(arms);
        return bean;
    }
}

IceFactory与WallFactory的工厂类同理。

然后我们回顾一下,之前的功能方法是下面这样的:

public class Function {
    public void put(String name, int number){
        Plant plant = SimpleFactory.createPlant(name);
        System.out.println("将"+plant.getName()+"放在了"+number+"号草地上");
        plant.fight();
    }
}

在我们修改了工厂类之后就不能再这样调用了,因为分不清到底该调用哪个工厂。

所以我们要新建一个工厂接口,让各个工厂类去实现该接口:

public interface Factory {
    public Plant createPlant();
}

为了决定应该调用哪一个工厂类,我们还需要一个构建工厂的方法,
所以再新建一个构造工厂类

public class FactoryBuilder {
    public static Factory build(String name){
        Factory factory = null;
        if (name.equals("BeanFactory")){
            factory = new BeanFactory();
        }else if (name.equals("IceFactory")){
            factory = new IceFactory();
        }else if (name.equals("WallFactory")){
            factory = new WallFactory();
        }
        return factory;
    }
}

然后修改Function类的调用方法即可:

public class Function {
    public void put(String name, int number){
        Factory factory = FactoryBuilder.build(name);
        Plant plant = factory.createPlant();
        System.out.println("将" + plant.getName() + "放在了" + number + "号草地上");
        plant.fight();
    }
}

好了,这样我们就实现了一个简单工厂模式的小例子,用一张图描述一下各个类的关系:

工厂模式是最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式,在实际项目开发中都可以使用这种模式,这种思路。



微信公众号【编程资源网】
本公众号致力于免费分享全网最优秀的视频资源,学习资料,面试经验等,前端,PHP,JAVA,算法,Python,大数据等等,你想要的这都有,还会分享优质博文,提高你的认知与思维

编程资源网-QQ交流群:625494093
要进微信交流群的话加微信:lovebugs88
微信搜索公众号:编程资源网 或者扫描下方二维码直接关注,

原创文章,转载请注明出处

上一篇 下一篇

猜你喜欢

热点阅读