java圈复杂度优化——枚举/策略模式

96 阅读4分钟

下列为初始代码:

package org.example;

public class GodShop {
    private final String WATER = "water";
    private final String PEN = "pen";
    private final String HAT = "hat";
    private String name;
    private int days;
    private int quality;

    public GodShop(String name, int days, int quality) {
        this.name = name;
        this.days = days;
        this.quality = quality;
    }

    private void handleWater() {
        this.quality++;
    }

    private void handlePen() {
        System.out.println("pen");
    }

    private void handleHat() {
        this.quality = Math.max(this.quality++, 5);
    }

    public void parse() {
        this.days--;
        switch (this.name) {
            case WATER:
                handleWater();
                break;
            case PEN:
                handlePen();
                break;
            case HAT:
                handleHat();
                break;
            default:
                this.quality++;
                break;
        }
    }

    public static void main(String[] args) {
        GodShop godShop = new GodShop("南瓜", 5, 10);
    }
}

使用枚举类型优化

package org.example;

import java.util.HashMap;
import java.util.Map;

public class GodShop {
    private static final Map<Goods, GoodsHandler> HANDLERS = new HashMap<>();

    static {
        HANDLERS.put(Goods.WATER, new GoodsHandler() {
            @Override
            public void handle(GodShop shop) {
                shop.quality++;
            }
        });

        HANDLERS.put(Goods.PEN, new GoodsHandler() {
            @Override
            public void handle(GodShop shop) {
                System.out.println("pen");
            }
        });

        HANDLERS.put(Goods.HAT, new GoodsHandler() {
            @Override
            public void handle(GodShop shop) {
                shop.quality = Math.max(shop.quality++, 5);
            }
        });

        HANDLERS.put(Goods.BOOK, new GoodsHandler() {
            @Override
            public void handle(GodShop shop) {
                System.out.println("Handling book...");
                shop.quality += 2;
            }
        });
    }

    public enum Goods {
        WATER, PEN, HAT, BOOK
    }

    interface GoodsHandler {
        void handle(GodShop shop);
    }

    private String name;
    private int days;
    private int quality;

    public GodShop(String name, int days, int quality) {
        this.name = name;
        this.days = days;
        this.quality = quality;
    }

    public void parse() {
        this.days--;

        try {
            Goods goods = Goods.valueOf(name.toUpperCase());

            GoodsHandler handler = HANDLERS.getOrDefault(goods, new GoodsHandler() {
                @Override
                public void handle(GodShop shop) {
                    shop.quality++;
                }
            });

            handler.handle(this);
        } catch (IllegalArgumentException e) {
            System.err.println("Invalid goods name: " + name);
            quality++; // 默认增加质量
        }
    }

    public static void main(String[] args) {
        GodShop godShop = new GodShop("invalid", 5, 10);
        godShop.parse();
        System.out.println("Quality after handling: " + godShop.quality);
    }
}
  1. 枚举类型:
    • 定义了一个枚举 Goods 来替代字符串 "water""pen""hat"。这使得代码更具类型安全性,也更容易维护。
  2. 处理器接口:
    • 创建了一个 GoodsHandler 接口,定义了一个 handle 方法来处理不同的商品。
  3. 映射处理器:
    • 使用 HashMap 来存储每个商品对应的处理器。这样可以轻松地添加新的商品及其处理逻辑。
  4. parse 方法:
    • 重构了 parse 方法,使用 valueOf 方法将商品名称转换为枚举类型。
    • 从 HANDLERS 映射中查找相应的处理器,并调用 handle 方法。
  5. 默认处理器:
    • 如果找不到特定的商品处理器,则使用一个默认处理器,该处理器简单地增加商品的质量。
  6. 拓展性:
    • 如果需要添加新的商品,只需要在 Goods 枚举中添加新的条目,并在 HANDLERS 映射中添加相应的处理器即可。

优点

  • 降低圈复杂度:通过将逻辑分散到不同的处理器中,减少了 parse 方法中的条件分支。
  • 提高可读性和可维护性:枚举和处理器的使用使得代码更加清晰易懂。
  • 易于拓展:添加新的商品只需要修改很少的代码。

这样的重构不仅提高了代码的质量,而且使得未来的维护和扩展变得更加容易。

使用策略模式

package org.example;

import java.util.HashMap;
import java.util.Map;

public class GodShop {
    private final String name;
    private int days;
    public int quality;

    public GodShop(String name, int days, int quality) {
        this.name = name;
        this.days = days;
        this.quality = quality;
    }

    public void parse() {
        this.days--;

        try {
            GoodsStrategy strategy = getStrategyForName(name);

            if (strategy != null) {
                strategy.execute(this);
            } else {
                quality++; // 默认情况下,增加质量
            }
        } catch (Exception e) {
            System.err.println("Error while parsing the name: " + name);
        }
    }

    private GoodsStrategy getStrategyForName(String name) {
        return STRATEGIES.getOrDefault(name.toUpperCase(), null);
    }

    private static final Map<String, GoodsStrategy> STRATEGIES = new HashMap<>();

    static {
        STRATEGIES.put("WATER", new WaterStrategy());
        STRATEGIES.put("PEN", new PenStrategy());
        STRATEGIES.put("HAT", new HatStrategy());
        STRATEGIES.put("BOOK", new BookStrategy());
    }

    public static void main(String[] args) {
        GodShop godShop = new GodShop("WATER", 5, 10);
        godShop.parse();
        System.out.println("Quality after handling: " + godShop.quality);
    }
}

interface GoodsStrategy {
    void execute(GodShop shop);
}

class WaterStrategy implements GoodsStrategy {
    @Override
    public void execute(GodShop shop) {
        shop.quality++;
    }
}

class PenStrategy implements GoodsStrategy {
    @Override
    public void execute(GodShop shop) {
        System.out.println("pen");
    }
}

class HatStrategy implements GoodsStrategy {
    @Override
    public void execute(GodShop shop) {
        shop.quality = Math.max(shop.quality++, 5);
    }
}

class BookStrategy implements GoodsStrategy {
    @Override
    public void execute(GodShop shop) {
        System.out.println("Handling book...");
        shop.quality += 2;
    }
}

** 代码解析**

  1. 策略接口 (GoodsStrategy):
    • 定义了一个 execute 方法,用于执行特定商品的策略。
  2. 具体策略类 (WaterStrategy, PenStrategy, HatStrategy, BookStrategy):
    • 每个类都实现了 GoodsStrategy 接口,并提供了不同的 execute 方法实现。
  3. 策略映射 (STRATEGIES):
    • 使用 HashMap 来存储每个商品名称与其对应的策略对象。
    • 在静态初始化块中填充这个映射。
  4. parse 方法:
    • 获取商品名称对应的策略对象。
    • 调用策略对象的 execute 方法来执行具体的策略。
  5. 拓展性:
    • 如果需要添加新的商品,只需要创建一个新的策略类,并在 STRATEGIES 映射中添加相应的条目即可。 优点
  • 降低圈复杂度:通过将不同的策略分离到独立的类中,减少了 parse 方法中的条件分支。
  • 提高可读性和可维护性:每个策略类只关注一种商品的处理逻辑,使得代码更加清晰。
  • 易于拓展:添加新的商品只需要定义新的策略类,并在 STRATEGIES 映射中添加条目即可。 这样的重构不仅提高了代码的质量,而且使得未来的维护和扩展变得更加容易。