前言:在最近的后端开发中,多多少少会发现有很多if-else语句,如果条件过多则会造成整体代码看起来非常臃肿,这边我就举一个我在实际开发中的例子,来进行阐述这两种模式在实际开发中我是如何运用的。
目录
一、工厂模式简介
二、简单工厂模式
2.1、UML类图
2.2、角色设计
2.3、代码实现
2.4、总结
三、工厂方法模式
3.1、UML类图
3.2、角色设计
3.2、代码实现
3.3、总结
四、抽象工厂模式
4.1、UML类图
4.2、角色设计
4.3、代码实现
4.4、总结
五、策略模式
5.1、UML类图
5.2、角色设计
5.3、代码实现
5.4、总结
六、使用工厂模式+策略模式消除if-else语句
一、SpringBoot项目(推荐)
二、普通的Java项目
七、总结
工厂模式将目的将创建对象的具体过程屏蔽隔离起来,从而达到更高的灵活性,具体可以分为三种来进行逐一介绍,分别是简单工厂模式、工厂方法模式以及抽象工厂模式。这种模式可以使得我们代码的整体结构更加信息,有效的动态进行封装以及降低代码之间的耦合度。
角色 | 描述 |
---|---|
简单工厂 | 负责创建所有实体类的内部逻辑,工厂类创建产品的方法可以直接被外部调用,以此来创建所修要的产品对象 |
抽象水果 | 简单工厂模式创建对象的公用接口 |
具体水果 | 公用接口创建的具体对象 |
1、自定义一个水果接口
public interface Fruit {
public void name();
}
2、自定义水果类-苹果
public class Apple implements Fruit{
@Override
public void name() {
System.out.println("苹果");
}
}
3、自定义水果类-西瓜
public class Watermelon implements Fruit{
@Override
public void name() {
System.out.println("西瓜");
}
}
4、创建一个简单的水果工厂,通过传递进来的水果名词进行生产水果
public class FruitFactory {
public static Fruit makeFruit(String name){
if("苹果".equals(name)){
return new Apple();
}else if ("西瓜".equals(name)){
return new Watermelon();
}else {
return null;
}
}
}
5、市场类
public class Market {
public static void main(String[] args) {
Fruit fruit = FruitFactory.makeFruit("西瓜");
fruit.name();
}
}
优点:简单工厂模式封装了创建对象的逻辑,完成了创建对象逻辑和业务代码之间的解耦。
缺点:每个具体的水果类的接口增多时,会需要在简单工程类中新增大批的对象生产的方法,这样在抽象工厂当中会有很多的if-else语句,同时也违背了“对修改关闭,对扩展开启的”原则(开闭原则)
角色 | 描述 |
抽象水果工厂 | 定义了创建水果的方法,任何模式中在创建水果的工厂类必须实现这个接口 |
具体水果工厂 | 实现抽象水果工厂接口的具体水果工厂类,负责生产具体的水果产品 |
抽象水果 | 水果对象公用的接口 |
具体水果 | 实现了抽象水果定义的接口,某具体的水果由某具体的工厂进行创建 |
1、定义水果工厂接口
public interface FruitFactory {
Fruit makeFruit();
}
2、定义水果接口
public interface Fruit {
public void name();
}
3、自定义苹果实现类
public class Apple implements Fruit{
@Override
public void name() {
System.out.println("苹果");
}
}
4、自定义西瓜实现类
public class Watermelon implements Fruit{
@Override
public void name() {
System.out.println("西瓜");
}
}
5、自定义苹果生产工厂
public class AppleFactory implements FruitFactory{
@Override
public Fruit makeFruit() {
return new Apple();
}
}
6、自定义西瓜生产工厂
public class WatermelonFactory implements FruitFactory{
@Override
public Fruit makeFruit() {
return new Watermelon();
}
}
7、市场类
public class Market {
public static void main(String[] args) {
AppleFactory appleFactory = new AppleFactory();
appleFactory.makeFruit().name();
}
}
优点:在简单工厂模式的基础上进行优化,完美符合了“开闭原则”和“单一职责原则”,便于进行扩展。
缺点:类的个数容易过多,增加代码的复杂度并且抽象工厂接口的具体工厂只可以生产单独一个类型的产品。
角色 | 描述 |
---|---|
抽象工厂 | 声明生产水果的一个工厂接口(可以声明生产多个类型产品的方法) |
具体工厂 | 实现生产水果的具体实体类 |
抽象水果 | 水果对象公用的接口 |
具体水果 | 实现了抽象水果定义的接口,某具体的水果由某具体的工厂进行创建 |
1、定义水果和包装接口
public interface Fruit {
public void name();
}
public interface Package {
public void name();
}
2、定义水果工厂接口
public interface FruitFactory {
Fruit makeFruit();
Package makePackage();
}
3、 定义苹果实现类以及对应的苹果包装实现类
public class Apple implements Fruit {
@Override
public void name() {
System.out.println("苹果");
}
}
public class ApplePackage implements Package{
@Override
public void name() {
System.out.println("苹果包装");
}
}
4、 定义西瓜实现类以及对应的西瓜包装实现类
public class Watermelon implements Fruit {
@Override
public void name() {
System.out.println("西瓜");
}
}
public class WatermelonPackage implements Package{
@Override
public void name() {
System.out.println("西瓜包装");
}
}
5、 自定义苹果工厂
public class AppleFactory implements FruitFactory {
@Override
public Fruit makeFruit() {
return new Apple();
}
@Override
public Package makePackage() {
return new ApplePackage();
}
}
6、自定义西瓜工厂
public class WatermelonFactory implements FruitFactory {
@Override
public Fruit makeFruit() {
return new Watermelon();
}
@Override
public Package makePackage() {
return new WatermelonPackage();
}
}
7、市场类
public class Market {
public static void main(String[] args) {
FruitFactory appleFactory = new AppleFactory();
FruitFactory watermelonFactory = new WatermelonFactory();
appleFactory.makeFruit().name();
appleFactory.makePackage().name();
watermelonFactory.makeFruit().name();
watermelonFactory.makePackage().name();
}
}
优点:抽象工厂模式在工厂方法模式进行了进一步延伸,都是符合“开闭原则”,但是与工厂方法模式不同的是,工厂方法模式在新增一个具体的产品时候会需要增加对应的工厂,但是抽象工厂模式只有在新增一个系列的具体产品才需要新增工厂,就是说工厂方法模式只可以创建一个单一的具体产品,而抽象工厂模式可以创建多个且属于一个类型的具体产品。
缺点:产品族扩展比较繁琐,当新增一个系列的某一个产品时,需要增加具体的产品类,还需要增加对应的工厂类。
策略模式是一种行为型模式,它将对象和行为拆开,将具体的行为定义为一个行为接口和具体的实现,把它们一个个封装起来,并且可以使他们互相替换。
角色 | 描述 |
---|---|
策略抽象接口 | 用于定义若个算法的表示 |
具体策略类 | 是策略抽象接口的具体实现,说白了就是重写策略的方法 |
策略上下文类 | 上下文包含用策略(接口)声明的变量,委托策略变量调用具体策略所实现的策略接口中的方法 |
1、定义支付策略接口
public interface PayStrategy {
void pay();
}
2、微信支付策略实现类
public class WXStrategy implements PayStrategy{
@Override
public void pay() {
System.out.println("微信支付");
}
}
3、支付宝策略实现类
public class ZFBStrategy implements PayStrategy{
@Override
public void pay() {
System.out.println("支付宝支付");
}
}
4、策略上下文实现
public class StrategyContext {
private PayStrategy payStrategy;
public StrategyContext(PayStrategy payStrategy) {
this.payStrategy = payStrategy;
}
public void pay(){
this.payStrategy.pay();
}
}
5、主方法测试
public class Main {
public static void main(String[] args) {
PayStrategy wx = new WXStrategy();
PayStrategy zfb = new ZFBStrategy();
StrategyContext wxContext = new StrategyContext(wx);
StrategyContext zfbContext = new StrategyContext(zfb);
wxContext.pay();
zfbContext.pay();
}
}
优点:策略模式完全符合了“开闭原则”,用户可以在不修改原有系统基础的选择上,可以灵活的切换对应的行为,同时也提供了管理相关的算法族的方法和替换集成关系的方法,最后也避免了多重的条件语句。
缺点:策略模式会产生很多的策略类,客户端必须要知道所有的策略类来自我决定要使用哪一种策略。
这边例如有一个需求:后端需要接收前端传递过来的修改的字段类型和修改具体内容去修改某个指定的字段,这边列举了两个例子,需要修改一个人的姓名或者是住址,如果是用传统的if-else语句是这样写的.
String bulkType = "address";
String bulkStr = "上海";
if("name".equals(bulkType)){
System.out.println("修改姓名为:" + bulkStr);
} else if ("address".equals(bulkType)) {
System.out.println("修改住址为:"+ bulkStr);
}
如果修改的类型有5个甚至更多,那就会有很多冗余的if-else语句,这样就会显得整体代码不够优雅,非常臃肿,所以这边就需要我们用到java的设计模式去优化掉这段if-else语句。
1、策略接口
package com.example.strategic.service;
public interface Revise {
public void execute(String str);
}
2、姓名修改策略类:
需要打上@Component注解交给Spring容器去管理并标识唯一的Bean名称。
package com.example.strategic.service;
import org.springframework.stereotype.Component;
@Component("name")
public class ReviseName implements Revise{
@Override
public void execute(String str) {
System.out.println("修改姓名为:" + str);
}
}
3、地址修改策略类:
package com.example.strategic.service;
import org.springframework.stereotype.Component;
@Component("address")
public class ReviseAddress implements Revise{
@Override
public void execute(String str) {
System.out.println("修改住址为:"+str);
}
}
4、策略工厂类
通过@Autowired按类型自动注入一个Map,Spring会自动将这些Bean根据其name作为键,实例作为值,注入到reviseMap这个Map中。
getReviseStrategy方法根据传入的bulkType参数作为键,从reviseMap中获取对应的Revise实现类实例。
调用者只需要传入bulkType即可,不需要关心具体实现类,实现了面向接口编程。
package com.example.strategic.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
public class ReviseFactory {
@Autowired
private Map reviseMap;
public Revise getReviseStrategy(String bulkType) throws Exception {
Revise reviseStrategy = reviseMap.get(bulkType);
if(reviseStrategy == null){
throw new Exception("错误的选项!");
}
return reviseStrategy;
}
}
5、测试运行
@SpringBootTest
class StrategicApplicationTests {
@Resource
ReviseFactory bulkFactory;
@Test
void contextLoads() throws Exception {
Revise revise = bulkFactory.getReviseStrategy("address");
revise.execute("上海");
}
}
6、运行结果
1、策略接口
public interface Revise {
public void execute(String str);
}
2、姓名修改策略类
public class ReviseName implements Revise{
@Override
public void execute(String str) {
System.out.println("修改姓名为:" + str);
}
}
3、地址修改策略类
public class ReviseAddress implements Revise{
@Override
public void execute(String str) {
System.out.println("修改住址为:"+str);
}
}
4、策略工厂类
public class ReviseFactory {
private static Map map = new HashMap<>();
static {
map.put("name", new ReviseName());
map.put("address", new ReviseAddress());
}
public static Revise getReviseStrategy(String str) {
return map.get(str);
}
}
5、测试运行
public static void main(String[] args) {
String bulkType = "address";
String bulkStr = "上海";
//获取对应的策略接口
Revise revise = ReviseFactory.getReviseStrategy(bulkType);
// 调用具体策略方法
revise.execute(bulkStr);
}
6、运行结果
本篇博客汇总了我对工厂模式和策略模式的理解,以及我是如何把它们巧妙运用在实际项目当中的一些技巧进行了分享,如有问题,欢迎评论区讨论!