在java

时间:2017-11-08 21:07:00

标签: java design-patterns

我正在学习java设计模式,我想知道是否可以应用以下问题。我有班级Solider和一些儿童班,例如:将军和警长。我正在创建Solider对象,在运行时我想将此对象更改为General或Sergeant对象,或者使用之前创建的Solider对象创建新的Sergeant或General对象:

 Solider s = new Solider(...);
 .....
 if (generalCondition) {
     General g = createGeneralFromSolider(s);
     //or better:
     //General g = promoteSoliderToGeneral(s);
 } else if (sergeantCondition) {
      Sergeant sr = createSergeantFromSolider(s);
      //or better:
      //Sergeant sr = promoteSoliderToSergeant(s);
 }

首先,我决定在General / Sergeant Class中创建额外的构造函数:

Class General extends Solider {
    General(Solider s, Map<String, String> generalSpecificParams) {
        //first we are going to copy all solider params to general params (bad idea if we have a lot of params)
        this.setParamX(s.getParamX());
        ....
        //then we can assign the rest of general-specific params
        this.setGeneralSpecificParams(generalSpecificParams);
    }
}

并在方法createGeneralFromSolider中使用它,但我不确定它是否是优雅的方式。主要的缺点是我创建了新对象,所以在调用createGeneralFromSolider后我在内存中有2个对象。我宁愿在记忆中有一个物体:一般/警长从Solider升级(对象将军/警长,之前是Solider对象)。我想知道我是否可以使用一些设计模式来解决它。我记得在C ++中有类似复制构造函数的东西,它通过一个接一个地分配所有参数来将所有参数从一个对象复制到另一个对象。在Java中,我没有听说过类似的东西。

4 个答案:

答案 0 :(得分:2)

您可能需要在这种情况下使用Factory模式。 例如:

public class SoldierFactory {

   //use getSoldier method to get object of type Soldier 
   public Soldier getSoldier(String soldierType){
      if(soldierType == null){
         return null;
      }     
      if(soldierType.equals("case1")){
         return new General();

      } else if(soldierType.equals("case2")){
         return new Sergeant();

      } else if(.....

      }

      return null;
   }
}




public class FactoryPatternDemo {

   public static void main(String[] args) {
      SoldierFactory soldierFactory = new SoldierFactory();

      Soldier s1 = soldierFactory.getsoldier("case1");


   }
}

我认为最好不要在召唤Soldier工厂之前创建士兵。你是否会在运行期间改变它?

答案 1 :(得分:2)

首先,在构造子类时,使用super作为构造函数的第一个语句,如下所示:

class Soldier {
    private String rank; // e.g. Pvt, PFC, etc.
    private int yearsOfService;

    // ... (Standard constructor)
    public Soldier(Soldier s) {
        this.rank = s.rank; this.yearsOfService = s.yearsOfService;
    }
    // ... (Getters and Setters)
}

class Sergeant extends Soldier {
    private int subordinates;

    public Sergeant(Soldier s) {
        super(s)
        this.rank = "Sergeant"; // overwrites this Sergeant's rank
        this.subordinates = 0;
    }
}

您可以轻松地将其封装在promoteSoldierToSergeant方法中。但是,如果具有许多属性的类被天真地设计,或者需要基于地图的变通方法,这可能会导致伸缩构造函数。要解决这个问题,我个人非常喜欢Builder pattern,但你也可以考虑Factory pattern

关于“复制构造函数”的问题可能最好通过阅读Clonable interface来解决,但要注意differences between shallow and deep copies,以及对类和数据结构的影响。

答案 2 :(得分:1)

我认为您的方法完全可以接受。如果你有一个对象X,你想要变成Y,你可以在Y构造函数中复制所有必要的字段。

您也可以使用构建器或静态工厂方法,但无论哪种方式,您都必须复制字段,因为java中没有自动复制构造函数(除非您使用一些专用库,如lombok,它可以提供来自注释的全args构造函数)

您担心内存中有2个对象。但如果你删除原始士兵的每一个参考,垃圾收集器就会销毁它。

最后一件事,正如@tsolakp所提到的,让General继承士兵是一个很好的设计吗?难道不能只是一个“等级”变量,或类似的东西,以反映这种状态?在构成足够的情况下过度使用继承是一个常见的错误,并且会减少麻烦。

答案 3 :(得分:1)

使用Reflections可以实现您的目标。

这样,您就可以自动将字段实例中的字段复制到子类。

您的代码看起来像这样:

public static void copyObject(Object src, Object dest)
            throws IllegalArgumentException, IllegalAccessException,
            NoSuchFieldException, SecurityException {
        for (Field field : src.getClass().getFields()) {
            dest.getClass().getField(field.getName()).set(dest, field.get(src));
        }
    }

    public static General createGeneral (Solider solider, String devision) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
        General general = new General();
        copyObject(solider, general);
        general.setDevision(devision);

        return general;
    }

Field 导入是java.lang.reflect.Field;

=============================================== =========================

另一种方法是使用Apache Bean Utils

然后,您可以像这样使用它的cloneBean(Object Bean)方法:

General general = cloneBean(solider);

将字段从solider复制到general,然后将所有字段复制到子类(General)。

=============================================== =========================

编辑:引入另一个用于&#34;普通&#34;的儿童类也是明智的。如果您打算将父类 Solider 用于&#34;普通&#34; soliders(我想你根据你的评论方法名称​​ promoteSoliderToGeneral(Solider s)进行。

因此,例如,您将拥有一个名为 MilitaryMan 的父类和3个扩展它的子类: Solider General 警长

这样,您就可以统一处理所有 MilitaryMan 。并且,您可以检查 MilitaryMan Solider General 还是 Sergeant

if (militaryMan instanceOf Solider) {
   // do solider specific processing
   ...
} else if (militaryMan instanceof General) {
   ...
} else if (militaryMan instanceof Sergeant) {
   ...
}

我认为这样会更清洁。