让代码变美的第四天 - 工厂方法模式

话接上回【让代码变美的第三天 - 简单工厂模式】
简单工厂其实还是不够完美,破坏了程序的开放封闭,那么看下工厂方法模式如何解决

丑陋的模样

简单工厂代码

    public static Fruit getFruit(String name) {
        if ("苹果".equals(name)) {
            Apple apple = new Apple();
            // 洗苹果 + 切苹果
            // todo other init
            return apple;
        } else if ("菠萝".equals(name)) {
            Pineapple pineapple = new Pineapple();
            // 切菠萝
            // todo other init
            return pineapple;
        } else {
            throw new RuntimeException("没有找到该水果");
        }
    }

重构

抽象工厂

public interface FruitFacory {
    Fruit getFruit();
}

定义具体工厂

public class AppleFacory implements FruitFacory {
    @Override
    public Fruit getFruit() {
        Apple apple = new Apple();
        // 洗苹果 + 切苹果
        // todo other init
        return apple;
    }
}
public class PineappleFacory implements FruitFacory {
    @Override
    public Fruit getFruit() {
        Pineapple pineapple = new Pineapple();
        // 切菠萝
        // todo other init
        return pineapple;
    }
}

使用

    public void sendApple() {
        AppleFacory appleFacory = new AppleFacory();
        Fruit apple = appleFacory.getFruit();
//        Fruit apple = FruitFacory.getFruit("苹果");
        sendPeople(apple);
    }

    public void eatApple() {
        AppleFacory appleFacory = new AppleFacory();
        Fruit apple = appleFacory.getFruit();
//        Fruit apple = FruitFacory.getFruit("苹果");
        eatPeople(apple);
    }

    public void eatPineapple() {
        AppleFacory appleFacory = new AppleFacory();
        Fruit pineapple = appleFacory.getFruit();
//        Fruit pineapple = FruitFacory.getFruit("菠萝");
        eat(apple);
    }

注释掉的是简单工厂的获取方式,可以简单进行对比,工厂方法模式无非就是对每个产品包装了一层,这一层就是具体的工厂
UML图(网上借用的)如下:
让代码变美的第四天 - 工厂方法模式_第1张图片

新问题

每次获取产品的时候,都要精确知道是哪个工厂生产的,不管要记住产品的名字,还要记住工厂的名字,这不是给工作增加负担么?
有没有一种办法,使得客户不用关心具体是哪个工厂生产的呢?就和简单工厂模式一样简单呢?

再次重构

只讨论JAVA场景,Spring项目,利用Spring优雅的解决上面的问题。
其实在【让代码变美的第一天 - 观察者模式】中也是使用类似这种办法。
重新定义产品名(简单工厂中使用的产品名)

public interface FruitFacory {
    String getFruitName();

    Fruit getFruit();
}
@Component
public class AppleFacory implements FruitFacory {
    @Override
    public Fruit getFruit() {
        Apple apple = new Apple();
        // 洗苹果 + 切苹果
        // todo other init
        return apple;
    }

    @Override
    public String getFruitName() {
        return "苹果";
    }
}
@Component
public class PineappleFacory implements FruitFacory {
    @Override
    public Fruit getFruit() {
        Pineapple pineapple = new Pineapple();
        // 切菠萝
        // todo other init
        return pineapple;
    }

    @Override
    public String getFruitName() {
        return "菠萝";
    }
}
  • 产品名也可以使用【让代码变美的第一天 - 观察者模式】中的注解来实现,注解或许x格高一点。

构建产品名和工厂的映射关系

@Service
public class FruitService {
    public static Map<String, FruitFacory> fruitFactoryMap = new HashMap<>();

    @Autowired
    private List<FruitFacory> fruitFactoryList;

    @PostConstruct
    private void init() {
        for (FruitFacory fruitFacory : fruitFactoryList) {
            fruitFactoryMap.put(fruitFacory.getFruitName(), fruitFacory);
        }
    }

    public static Fruit getFruit(String fruitName) {
        return fruitFactoryMap.get(fruitName).getFruit();
    }
}

使用

    public void sendApple() {
        Fruit apple = FruitService.getFruit("苹果");
//        AppleFacory appleFacory = new AppleFacory();
//        Fruit apple = appleFacory.getFruit();
//        Fruit apple = FruitFacory.getFruit("苹果");
//        sendPeople(apple);
    }

    public void eatApple() {
        Fruit apple = FruitService.getFruit("苹果");
//        AppleFacory appleFacory = new AppleFacory();
//        Fruit apple = appleFacory.getFruit();
//        Fruit apple = FruitFacory.getFruit("苹果");
//        eatPeople(apple);
    }

看这里是不是又和简单工厂模式差不多了,外部不用关心产品内部细节,也不需要关心需要用哪个工厂,只要通过名称获取到产品即可
再来看有没有破坏开放封闭原则,我们新增一个产品,比如:香蕉。

  • 只需要新增Banana(产品)、BananaFacory(工厂)。

Spring帮我们解决的是扫描所有工厂类(当然我们自己利用反射也能做),让我们无需在新增工厂的时候去加if else

你可能感兴趣的:(代码重构-设计模式,工厂方法模式,开发语言,java)