目录
一、工厂模式是什么?
说到工厂模式,就要讲到什么是工厂;提到工厂,就要讲到社会大生产的分工;提到分工...那就要从人类起源讲起了,一篇文章就不够絮叨的了。简单一句话,工厂就是为了提高生产效率,将生产与使用的过程分开的一个场所,具体到java开发中来讲就是把对象的创建过程和使用过程分开的一种表现形式。
二、准备工作
我们以兵器制造为例,先创建两个兵器类:机枪&坦克
package com.elean.design;
/**
* @auther Elean
* @date 19/6/2024 下午9:25
* @description 兵器
*/
public interface Weaponry {
void fire();
void ammunitionLoading();
void maintenance();
}
package com.elean.design;
/**
* @auther Elean
* @date 19/6/2024 下午9:27
* @description 枪
*/
public class Gun implements Weaponry{
@Override
public void fire() {
System.out.println("机枪开火:扣动扳机。。。");
}
@Override
public void ammunitionLoading() {
System.out.println("机枪装弹:把子弹装进弹夹。。。");
}
@Override
public void maintenance() {
System.out.println("机枪保养:拆开擦一擦。。。");
}
}
package com.elean.design;
/**
* @auther Elean
* @date 19/6/2024 下午9:27
* @description 坦克
*/
public class Tank implements Weaponry{
@Override
public void fire() {
System.out.println("坦克开火:开炮。。。");
}
@Override
public void ammunitionLoading() {
System.out.println("坦克装弹:装进掷弹筒。。。");
}
@Override
public void maintenance() {
System.out.println("坦克保养:洗刷刷。。。");
}
}
三、传统的工厂模式
当不使用工厂模式时,大兵们只有自己动手,才能丰衣足食,自己造枪,自己造炮
package com.elean.design;
/**
* @auther Elean
* @date 20/6/2024 下午2:53
* @description
*/
public class Soldier {
public static void main(String[] args) {
//造枪-保养-装弹-开火
Gun gun = new Gun();
gun.maintenance();
gun.ammunitionLoading();
gun.fire();
//造炮-保养-装弹-开火
Tank tank = new Tank();
tank.maintenance();
tank.ammunitionLoading();
tank.fire();
}
}
我仅代表和平战士们同情这些大兵。。。
1.简单工厂
简单工厂模式提供了最基础的工厂模型,它把所有的生产流水线集中到一个车间中完成。大兵向工厂提出自己的需要,由工厂根据需要在相应流水线生产武器给大兵使用。
package com.elean.design;
/**
* @auther Elean
* @date 19/6/2024 下午9:24
* @description 兵工厂
*/
public class Arsenal {
public Weaponry create(String weapon) {
switch (weapon) {
case "gun" :
return new Gun();
case "tank" :
return new Tank();
}
return null;
}
}
package com.elean.design;
/**
* @auther Elean
* @date 20/6/2024 下午2:53
* @description
*/
public class Soldier {
public static void main(String[] args) {
//造枪-保养-装弹-开火
Arsenal arsenal = new Arsenal();
Weaponry gun = arsenal.create("gun");
gun.maintenance();
gun.ammunitionLoading();
gun.fire();
//造炮-保养-装弹-开火
Weaponry tank = arsenal.create("tank");
tank.maintenance();
tank.ammunitionLoading();
tank.fire();
}
}
简单工厂初步实现了让大兵不再关注武器生产这事了,可以专注于发挥武器的最大威力。但是,对于工厂来说,在一间厂房里为每个武器都创建一条流水线是不明智的。武器的种类是在扩展的,抛开厂房扩建的成本不算,厂房扩建中也许会影响到当前流水线的正常使用,即使扩建完成,也保不齐哪天流水线就串了,维护成本比较高(你懂的)。
2.工厂方法模式
package com.elean.design;
/**
* @auther Elean
* @date 19/6/2024 下午9:24
* @description 兵工厂
*/
public interface Arsenal {
Weaponry create();
}
package com.elean.design;
/**
* @auther Elean
* @date 20/6/2024 下午8:28
* @description 机枪车间
*/
public class GunWorkshop implements Arsenal{
@Override
public Weaponry create() {
return new Gun();
}
}
package com.elean.design;
/**
* @auther Elean
* @date 20/6/2024 下午8:28
* @description 装甲车间
*/
public class TankWorkshop implements Arsenal{
@Override
public Weaponry create() {
return new Tank();
}
}
package com.elean.design;
/**
* @auther Elean
* @date 20/6/2024 下午2:53
* @description
*/
public class Soldier {
public static void main(String[] args) {
//造枪-保养-装弹-开火
Arsenal gunArsenal = new GunWorkshop();
Weaponry gun = gunArsenal.create();
gun.maintenance();
gun.ammunitionLoading();
gun.fire();
//造炮-保养-装弹-开火
Arsenal tankArsenal = new TankWorkshop();
Weaponry tank = tankArsenal.create();
tank.maintenance();
tank.ammunitionLoading();
tank.fire();
}
}
工厂方法模式解决了简单工厂扩展和维护(开闭原则:略)的问题,但是,随着专业化分工的发展,枪托,枪管等零部件的生产应该由一部分人专门负责,而流水线只负责零部件的组装。这样既减少了这部分人学习的压力, 又能让他们在专攻领域精益求精,何乐不为?专业细化的分工,也诞生了一部分零部件生产厂商,而同样的零部件也可以供应多种兵器的生产。我们来看看抽象工厂是如何做的
同理,在Java项目开发中,对于工厂方法模式于抽象工厂的区别,用产品等级结构和产品族来描述
3.抽象工厂
首先,我们创建机枪创建两个零件枪管&弹夹
package com.elean.design;
/**
* @auther Elean
* @date 21/6/2024 下午12:28
* @description 枪管
*/
public interface Barrel {
/**
* 口径
*/
void diameter();
}
package com.elean.design;
/**
* @auther Elean
* @date 21/6/2024 下午12:40
* @description
*/
public class Barrel762 implements Barrel{
@Override
public void diameter() {
System.out.println("口径:7.62mm");
}
}
package com.elean.design;
/**
* @auther Elean
* @date 21/6/2024 下午12:40
* @description
*/
public class Barrel1143 implements Barrel{
@Override
public void diameter() {
System.out.println("口径:11.43mm");
}
}
package com.elean.design;
/**
* @auther Elean
* @date 21/6/2024 下午12:28
* @description 弹夹
*/
public interface Clip {
/**
* 类型
*/
void type();
}
package com.elean.design;
/**
* @auther Elean
* @date 21/6/2024 下午12:36
* @description
*/
public class EnBlockClip implements Clip{
@Override
public void type() {
System.out.println("漏夹");
}
}
package com.elean.design;
/**
* @auther Elean
* @date 21/6/2024 下午12:36
* @description
*/
public class StripperClip implements Clip{
@Override
public void type() {
System.out.println("桥夹");
}
}
加工厂拿到图纸后决定为M16和汤姆逊机枪引进几条流水线生产这些零部件
package com.elean.design;
/**
* @auther Elean
* @date 21/6/2024 下午2:03
* @description 抽象零部件兵工厂
*/
public interface AbstractPartArsenal {
Barrel createBarrel();
Clip createClip();
}
package com.elean.design;
/**
* @auther Elean
* @date 21/6/2024 下午2:12
* @description M16零部件加工流水线
*/
public class M16PartArsenal implements AbstractPartArsenal {
@Override
public Barrel createBarrel() {
return new Barrel762();
}
@Override
public Clip createClip() {
return new StripperClip();
}
}
package com.elean.design;
/**
* @auther Elean
* @date 21/6/2024 下午2:12
* @description 汤普逊零部件加工流水线
*/
public class ThompsonPartArsenal implements AbstractPartArsenal {
@Override
public Barrel createBarrel() {
return new Barrel1143();
}
@Override
public Clip createClip() {
return new StripperClip();
}
}
下面,我们试着构建两个组装车间把枪械组装起来
package com.elean.design;
/**
* @auther Elean
* @date 19/6/2024 下午9:27
* @description 枪工厂
*/
public interface GunArsenal {
void assemble(AbstractPartArsenal part);
}
package com.elean.design;
/**
* @auther Elean
* @date 21/6/2024 下午3:43
* @description
*/
public class M16GunArsenal implements GunArsenal{
@Override
public void assemble(AbstractPartArsenal part) {
Barrel barrel = part.createBarrel();
Clip clip = part.createClip();
System.out.println("组装M16:");
barrel.diameter();
clip.type();
}
}
package com.elean.design;
/**
* @auther Elean
* @date 21/6/2024 下午3:43
* @description
*/
public class ThompsonGunArsenal implements GunArsenal{
@Override
public void assemble(AbstractPartArsenal part) {
Barrel barrel = part.createBarrel();
Clip clip = part.createClip();
System.out.println("组装汤姆逊:");
barrel.diameter();
clip.type();
}
}
经历了这么多的加工和组装的工序,大兵们总算拿到他们心仪的武器了。值得庆幸的是,他们完全不用关心生产的全过程
package com.elean.design;
/**
* @auther Elean
* @date 20/6/2024 下午2:53
* @description
*/
public class Soldier {
public static void main(String[] args) {
AbstractPartArsenal m16Part = new M16PartArsenal();
GunArsenal m16 = new M16GunArsenal();
m16.assemble(m16Part);
AbstractPartArsenal gunArsenal = new ThompsonPartArsenal();
GunArsenal thompson = new ThompsonGunArsenal();
thompson.assemble(gunArsenal);
}
}
四、用函数式接口写工厂模式
在实际开发过程中,我们对工厂模式的应用往往是从框架的角度出发的,但对于独立开发的程序员来说可能更加注重某个模块的功能实现。如果在功能模块的开发过程中,需要根据不同的入参条件使用不同的方法,这时候我们就需要对工厂模式的原理进行生活,以实现使用者只关注使用而忽略复杂的实现逻辑的过程,由此,我们可以结合函数式接口,对工厂模式进行杂交、进化
package com.elean;
/**
* @auther Elean
* @date 19/6/2024 下午5:48
* @description
*/
public class DemoUtil {
public static Object calcOne(Object in) {
return "This is index one result " + in;
}
public static Object calcTwo(Object in) {
return "This is index two result " + in;
}
public static Object calcThree(Object in) {
return "This is index three result " + in;
}
public static Object error(Object in) {
throw new RuntimeException("No calc method to " + in);
}
}
package com.elean;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* @auther Elean
* @date 19/6/2024 下午5:47
* @description
*/
public enum DemoEnum {
INDEX_ONE("INDEX_ONE", DemoUtil::calcOne),
INDEX_TWO("INDEX_TWO", DemoUtil::calcTwo),
INDEX_THREE("INDEX_THREE", DemoUtil::calcThree),
ERROR("Error", DemoUtil::error)
;
private String code;
private Function func;
DemoEnum(String code, Function func) {
this.code = code;
this.func = func;
}
public Function getFunc() {
return func;
}
public static DemoEnum type(String code) {
return Stream.of(values())
.filter(e -> e.code.equals(code))
.findFirst()
.orElse(ERROR);
}
}
package com.elean;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> codes = new ArrayList<>();
codes.add("INDEX_ONE");
codes.add("INDEX_TWO");
codes.add("INDEX_THREE");
codes.stream()
.map(e -> DemoEnum.type(e).getFunc().apply(e))
.forEach(System.out::println);
}
}
小结
程序员是网络生态世界的搬运工。在开发过程中,使用开发模式往往能够让代码看起来更加整洁、易维护,但并不是任何场景都适合生搬硬套的把整个开发模式搬过来。在合适的场景,进行一定的拓展和加工创新,才能让我们的项目更加有灵魂!