比使用大量吸气剂更好的方式/设计

时间:2015-05-05 07:12:51

标签: java oop

假设我有一个存放武器阿森纳的课程

public class Arsenal{

  List<RocketLauncher> rocketLaunchers;
  List<HandGrenade> handGrenades;
  List<LandMine> landMines;
  //Followed by a few more weapons

}

武器是枚举武器的一部分

现在,我正在尝试在其他地方显示摘要屏幕或报告,我正在准备一张地图。请参阅下面的代码段。我已经展示了两种方法

public Map<Weapon,Integer> generateReport(Arsenal arsenal){

 Map<Weapon,Integer> weaponCountMap = //suitable map impl

 //Technique 1
 //Create and use a larger num of getters from the Arsenal Class
 weaponCountMap.put(Weapon.HAND_GRENADE, arsenal.getHandGrenades);
 //.. and so on

 //Technique 2
 //Create a single method in Arsenal which returns a Map
 weaponCountMap = arsenal.getSummary();

 return weaponCountMap;
}

问题:只是我或者每个人都觉得使用大量的getter &#39; 。假设阿森纳存储了大约50种武器,它类似于50种方法。双人套装。

另外。我觉得使用第二种方法不太灵活,没有访问方法。

你们可以批判性地评估这两种方法,并可能提出新方法吗?

5 个答案:

答案 0 :(得分:3)

你的Arsenal里面没有硬编码类型的武器怎么样?以下是针对您的具体情况的heterogeneous container的简单实现。但是,由于我不熟悉enum中的Generics,所以这个实现是在你有Weapon及其子类时,例如HardGrenade extends Weapon等等。

public class Arsenal{
    private Map<Class<?>, Collection<?>> weaponArsenal;

    public <T extends Weapon> Collection<T> get(Class<T> weaponClass) {
        if (weaponArsenal.containsKey(weaponClass) {
            return (Collection<T>) weaponArsenal.get(weaponClass);
        }
        return new ArrayList<T>(); // to avoid checking null everytime in client code
    }

    public <T extends Weapon> void put(T weapon) {
        if (!weaponArsenal.containsKey(weapon.class)) {
            Collection<T> weaponList = // initialize correct collection here
            weaponArsenal.put(weapon.class, weaponList);
        }

        weaponArsenal.get(weapon.class).add(weapon);
    }
}

并在客户端代码中

Arsenal arsenal = new Arsenal();

arsenal.put(new HandGrenade());
arsenal.put(new RocketLauncher());

Collection<HandGrenade> handGrenades = arsenal.get(HandGrenade.class);
Collection<RocketLauncher> rocketLaunchers = arsenal.get(RocketLauncher.class);

答案 1 :(得分:2)

在武器库中,您可以复制地图而不是使用列表。然后在generateReport方法中,您可以遍历枚举并使用枚举值从地图中获取合适的列表。

这样的东西

阿森纳:

 Map<Weapon,List<Weapon>> weaponsMap;
 arsenalMap.put(Weapon.HAND_GRENADE,handGrenades);

生成报告:

for (Weapon weapon: Weapon.values()) {
 weaponCountMap.put(Weapon.HAND_GRENADE, arsenal.weaponsMap.get(weapon));
}

可能不是最佳解决方案,但你会删除一些吸气剂。

答案 2 :(得分:1)

如果你让阿森纳不可变并使用构建器构建它(为了避免使用一堆构造函数),你可以将实例变量公开。

这种方法允许您使用问题中的技术1但没有getter,并且仍然保持对象内部对象的状态管理。

public class Arsenal {

  public final List<RocketLauncher> rocketLaunchers;
  public final List<HandGrenade> handGrenades;
  public final List<LandMine> landMines;
  //Followed by a few more weapons

  private Arsenal(final Arsenal.Builder builder) {
      this.rocketLaunchers = Collections.unmodifiableList(builder.rocketLaunchers);
      this.handGrenades = Collections.unmodifiableList(builder.handGrenades );
      this.landMines= Collections.unmodifiableList(builder.landMines);
      // and so on
  }

  public static class Builder {
      private final List<RocketLauncher> rocketLaunchers = new ArrayList<>();
      private final List<HandGrenade> handGrenades = new ArrayList<>();
      private final List<LandMine> landMines = new ArrayList<>();

      public Builder rocketLaunchers(List<RocketLauncher> rocketLaunchers) {
          this.rocketLaunchers.addAll(rocketLaunchers);
          return this;
      }

      public Builder handGrenades(List<HandGrenade> handGrenades) {
          this.handGrenades.addAll(handGrenades);
          return this;
      }

      public Builder landMines (List<LandMines> landMines ) {
          this.landMines .addAll(landMines );
          return this;
      }

      public Arsenal build() {
          return new Arsenal(this);
      }
  }
}

您现在可以通过以下方式使用它。

List<RocketLauncher> rocketLaunchers = //whatever
Arsenal arsenal = new Arsenal.Builder().rocketLaunchers(rocketLaunchers).build();
....
weaponCountMap.put(Weapon.HAND_GRENADE, arsenal.handGrenades);
//.. and so on

武器库的所有字段都是非空的,无法修改。如果你在外部类中定义构建器(即不是Arsenal中的静态类),你的Arsenal类将非常小 - 只是字段和构造函数,加上逻辑方法。

答案 3 :(得分:1)

请查看此类其他帖子... Java Conventions: use getters/setters WITHIN the class?,了解何时可以使用getter而不是直接访问。

这是一个设计问题,取决于你想要在你的军火库中包含什么,以及你希望暴露给外界来查看你的武器库。

就像联合国试图检查你的武器库而你告诉他们嘿我不会告诉你我在武器库中处理的是什么武器但是我可以给你一份报告,那就是你向外界介绍的报告。现在,这取决于你想要发出什么样的报告,以及哪些武器将落在你的地图上。

现在看看你的第二种技巧,你是否计划将地图创建逻辑转移到你的阿森纳类中。

要回答的另一个设计问题......这个逻辑只是为了获得报告然后我的建议是将其排除在阿森纳类之外并保持清醒。 否则你最终可能会把所有类型的报告逻辑都输入到阿森纳类中,它会变得越来越重,可能会爆炸。

答案 4 :(得分:0)

请参阅https://projectlombok.org/https://github.com/google/auto/tree/master/value。 为每个武器添加单独的字段,Lombok或AutoValue将为您创建getters / setters / hashcode / equals / toString方法。