跳到主要内容

使用策略模式消除ifelse

有这样的场景,根据不同的套餐,有不同的计算方式,全部在一个函数里面,使用if+else不停的判断,导致一个方法堆了成百上千行,而且不同的service里面都有这个关于不同套餐的计算方式。为了解决这个问题,学习使用策略模式消除,使得代码遵循开闭原则,新增新的套餐会变得容易

原则:修改关闭,扩展开放

策略模式

目前的代码:

现在有一个coding函数,我们想要根据传入的codeType来进行判断使用那个编辑器coding,如果这样ifelse写的话,每次新加一个编辑器,这边都要进行修改,不符合软件设计的开闭原则。

public void coding(String codeType) {
if (Objects.equals("IDEA", codeType)) {
System.out.println("使用IDEA编码");
} else if (Objects.equals("Eclipse", codeType)) {
System.out.println("使用Eclipse编码");
}
//...
}

修改

我们先定义一个编码接口

public interface Program {
void coding();
}

然后去实现不同种的编码方案:

public class Eclipse implements Program{
@Override
public void coding() {
System.out.println("使用Eclipse编码");
}
}

public class IDEA implements Program{
@Override
public void coding() {
System.out.println("使用IDEA编码");
}
}

使用

定义一个操作类,注入Program

private Program program;

public StrategyPattern(Program program) {
this.program = program;
}

public Program getProgram() {
return program;
}

public void setProgram(Program program) {
this.program = program;
}

public void startCoding(){
program.coding();
}


测试

    public void coding(String codeType) {
switch (codeType) {
case "Eclipse":
new StrategyPattern(new Eclipse()).startCoding();
break;
case "IDEA":
new StrategyPattern(new IDEA()).startCoding();
break;
default:
System.out.println("使用其他IDE编码");
}
}

这样其实还是ifelse,但是代码会简洁很多更容易维护,下面进行消除

策略枚举

定义一个枚举类,表示有哪些分支:

public enum ProgramEnums {
ECLIPSE("Eclipse"),
IDEA("IDEA");

private String codeType;

ProgramEnums(String codeType) {
this.codeType = codeType;
}

public String getCodeType() {
return codeType;
}
}

定义一个工厂类,用来根据type获取对应的实现

public class ProgramFactory {
private static final Map<String, Program> PROGRAM_MAP = new HashMap<>();

static {
PROGRAM_MAP.put("Eclipse", new Eclipse());
PROGRAM_MAP.put("IDEA", new IDEA());
}

public static Map<String, Program> getProgramMap(String codeType) {
return PROGRAM_MAP;
}
}

使用:

public static void main(String[] args) {
String codeType = "IDEA";
Program programMap = ProgramFactory.getProgramMap(codeType);
programMap.coding();
}

策略模式+工厂模式+模版方法

上面的代码其实还是有点冗余问题,我们可以使用策略模式+工厂模式+模版方法接口,

定义接口

public interface Program {
void coding();

void testing();

ProgramEnums getProgramEnum();
}

定义模版方法

@Service
public abstract class AbstractProgram implements InitializingBean, Program {
@Override
public void coding() {
throw new UnsupportedOperationException("不支持的操作");
}

/**
* 禁止调用
*/
@Override
public void testing() {
throw new UnsupportedOperationException("不支持的操作");
}

// 子类必须提供枚举类型
@Override
public abstract ProgramEnums getProgramEnum();

@Override
public void afterPropertiesSet() throws Exception {
ProgramFactory.registerProgram(getProgramEnum().getCodeType(), this);
}
}

这里的好处是Service定义为Bean,然后通过afterPropertiesSet自动让子类把自己注册到Bean进行管理

定义工厂

@Component
public class ProgramFactory {
private static final Map<String, AbstractProgram> PROGRAM_MAP = new HashMap<>();

// 提供注册方法
public static void registerProgram(String codeType, AbstractProgram program) {
PROGRAM_MAP.put(codeType, program);
}

// 获取注册的Program
public static AbstractProgram getProgram(String codeType) {
AbstractProgram program = PROGRAM_MAP.get(codeType);
if (program == null) {
throw new IllegalArgumentException("Unsupported code type: " + codeType);
}
return program;
}

public static AbstractProgram getProgram(ProgramEnums programEnums) {
return getProgram(programEnums.getCodeType());
}
}

定义枚举

public enum ProgramEnums {
ECLIPSE("Eclipse"),
IDEA("IDEA");

private final String codeType;

ProgramEnums(String codeType) {
this.codeType = codeType;
}

public String getCodeType() {
return codeType;
}
}

实现策略IDEA

@Component
public class IDEAProgram extends AbstractProgram {

@Override
public void coding() {
System.out.println("IDEA编程");
}

@Override
public ProgramEnums getProgramEnum() {
return ProgramEnums.IDEA;
}
}

实现策略Eclipse

@Component
public class EclipseProgram extends AbstractProgram {

@Override
public void coding() {
System.out.println("Eclipse编程");
}

@Override
public ProgramEnums getProgramEnum() {
return ProgramEnums.ECLIPSE;
}
}

测试

@SpringBootTest
class MainTest {
@Test
void test() {
String codeType = "IDEA";
AbstractProgram program = ProgramFactory.getProgram(codeType);
program.coding();
}
}

结果:

image-20240908214616120