使用哪种模式来避免代码重复与对象值转换器

时间:2016-01-27 16:39:47

标签: java polymorphism abstract-class code-duplication

我想摆脱MyFacadeBean中的以下代码重复。请考虑以下情况:

public class FacadeBean implements Facade {

    @EJB
    private CrudService crudService;

    @Inject
    private FirstAssembler firstAssembler;
    @Inject
    private SecondAssembler secondAssembler;
    @Inject
    private ThirdAssembler thridAssembler;
    @Inject
    private FourthAssembler fourthAssembler;

    @Override
    public void save(FirstValue value) {
        FirstEntity entity = this.firstAssembler.transformToEntity(value);
        this.crudService.persist(entity);
    }

    @Override
    public void save(SecondValue value) {
        SecondEntity entity = this.secondAssembler.transformToEntity(value);
        this.crudService.persist(entity);
    }

    @Override
    public void save(ThirdValue value) {
        ThirdEntity entity = this.thirdAssembler.transformToEntity(value);
        this.crudService.persist(entity);
    }

    @Override
    public void save(FourthValue value) {
        FourthEntity entity = this.fourthAssembler.transformToEntity(value);
        this.crudService.persist(entity);
    }

}

public interface MyFacade {

    void save(FirstValue value);

    void save(SecondValue value);

}

使用CrudService

public interface CrudService {

    void persist(Object entity);

}

@Stateless
@Local(CrudService.class)
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public class CrudServiceBean implements CrudService {

    public static final String PERSISTENCE_UNIT_NAME = "my_persistence_unit";

    private EntityManager entityManager;

    @PersistenceContext(unitName = PERSISTENCE_UNIT_NAME)
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Override
    public void persist(Object entity) {
        this.entityManager.persist(entity);
    }

}

使用以下汇编程序:

public class FirstAssembler extends AbstractAssembler<FirstEntity> {

    public FirstEntity transformToEntity(FirstValue value) {
        if (value == null)
            return null;
        FirstEntity entity = new FirstEntity();
        transformAbstractValueToAbstractObject(value, entity);
        entity.setFixedRate(value.getFixedRate());
        entity.setStartDate(value.getStartDate());
        return entity;
    }

}

public class SecondAssembler extends AbstractAssembler<SecondEntity> {

    public SecondEntity transformToEntity(SecondValue value) {
        if (value == null)
            return null;
        SecondEntity entity = new SecondEntity();
        transformAbstractValueToAbstractObject(value, entity);
        entity.setTransactionType(value.getTransactionType());
        entity.setValueDate(value.getValueDate());
        return entity;
    }

}

public abstract class AbstractAssembler<T extends AbstractEntity> {

    protected void transformAbstractValueToAbstractObject(AbstractValue value, T object) {
        object.setUniqueId(value.getUniqueId());
        object.setNominalAmountValue(value.getNominalAmountValue());
    }

}

使用以下实体:

@Entity
public class FirstEntity extends AbstractEntity {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "ID")
    private Long id;
    @Column(name = "START_DATE")
    @Temporal(TemporalType.DATE)
    private Date startDate;
    @Column(name = "FIXED_RATE")
    @Digits(integer = 1, fraction = 10)
    private BigDecimal fixedRate;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Date getStartDate() {
        return startDate;
    }

    public void setStartDate(Date startDate) {
        this.startDate = startDate;
    }

    public BigDecimal getFixedRate() {
        return fixedRate;
    }

    public void setFixedRate(BigDecimal fixedRate) {
        this.fixedRate = fixedRate;
    }

}


@Entity
public class SecondEntity extends AbstractEntity {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "ID")
    private Long id;
    @Column(name = "VALUE_DATE")
    @Temporal(TemporalType.DATE)
    private Date valueDate;
    @Column(name = "TRANSACTION_TYPE")
    @Enumerated(EnumType.STRING)
    private TransactionType transactionType;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Date getValueDate() {
        return valueDate;
    }

    public void setValueDate(Date valueDate) {
        this.valueDate = valueDate;
    }

    public TransactionType getTransactionType() {
        return transactionType;
    }

    public void setTransactionType(TransactionType transactionType) {
        this.transactionType = transactionType;
    }

}

@MappedSuperclass
public abstract class AbstractEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    @Column(name = "TRANSACTION_NOM_AMOUNT_VALUE")
    @Digits(integer = 18, fraction = 5)
    @Min(0)
    private BigDecimal nominalAmountValue;

    public BigDecimal getNominalAmountValue() {
        return nominalAmountValue;
    }

    public void setNominalAmountValue(BigDecimal nominalAmountValue) {
        this.nominalAmountValue = nominalAmountValue;
    }

}

我尝试了以下方法:

public class FacadeBean implements Facade {
    @Inject
    private Assembler assembler;

    @Inject
    private AssemblerFactory assemblerFactory;

    @Override
    public <T extends AbstractValue> void save(T value) {
        Assembler assembler = assemblerFactory.createAssembler(value);
        AbstractEntity entity = assembler.transformToEntity(value);
        this.crudService.persist(entity);
    }
}

问题是AssemblerFactoryImpl和AssemblerImpl,我必须在其中执行instanceOf检查和转换......

另一个想法是让值知道使用哪个变换器(或如何变换)。但我希望价值是“愚蠢的”。

@Glenn Lane

public AbstractValue save(AbstractValue value) {
    AbstractAssembler<AbstractValue, AbstractEntity> assembler = new FirstAssembler();
    AbstractEntity entity = assembler.transformToEntity(value);
    AbstractValue result = assembler.transformToValue(entity);
    return result;
}
由于

不起作用

类型不匹配:无法从FirstAssembler转换为AbstractAssembler

5 个答案:

答案 0 :(得分:3)

我将此作为单独的答案发布,因为我并不认为为每个AbstractValue类型设置保存方法有任何问题。

首先,我们将为此示例建立您的基值类。我正在使用界面,所以我们不会在水面上浑浊。您的AbstractValue界面:

public interface AbstractValue
{
    int getUniqueId();
    double getNominalValue();
    <T> T accept(AbstractValueVisitor<T> visitor);
}

&#34;访客界面&#34;:

public interface AbstractValueVisitor<T>
{
    T visit(FirstValue value);
    T visit(SecondValue value);
    T visit(ThirdValue value);
    T visit(FourthValue value);
}

我知道您不希望将智能融入AbstractValue,但我们将添加一个规范... AbstractValue的所有具体实现(所有四个方法都以这种方式实现accept方法:

@Override
public <T> T accept(AbstractValueVisitor<T> visitor)
{
    return visitor.visit(this);
}

因此该方法实现四次次:在所有四个值类中,完全相同的方式。因为访问者接口知道所有具体实现,所以将为每个特定值类型调用适当的方法。所有这三个部分放在一起的是#34;访客模式&#34;。

现在我们将建立一个实体工厂。它的工作是在提供AbstractEntity

时创建适当的AbstractValue
public class AbstractEntityFactory
        implements AbstractValueVisitor<AbstractEntity>
{
    private static final AbstractEntityFactory INSTANCE;

    static
    {
        INSTANCE = new AbstractEntityFactory();
    }

    // Singleton pattern
    private AbstractEntityFactory()
    {
    }

    public static AbstractEntity create(AbstractValue value)
    {
        if (value == null)
        {
            return null;
        }
        AbstractEntity e = value.accept(INSTANCE);
        e.setNominalValue(value.getNominalValue());
        e.setUniqueId(value.getUniqueId());
        return e;
    }


    @Override
    public AbstractEntity visit(FirstValue value)
    {
        FirstEntity entity = new FirstEntity();
        // Set all properties specific to FirstEntity
        entity.setFixedRate(value.getFixedRate());
        entity.setStartDate(value.getStartDate());
        return entity;
    }

    @Override
    public AbstractEntity visit(SecondValue value)
    {
        SecondEntity entity = new SecondEntity();
        // Set all properties specific to SecondEntity
        entity.setTransactionType(value.getTransactionType());
        entity.setValueDate(value.getValueDate());
        return entity;
    }

    @Override
    public AbstractEntity visit(ThirdValue value)
    {
        ThirdEntity entity = new ThirdEntity();
        // Set all properties specific to ThirdEntity
        return entity;
    }

    @Override
    public AbstractEntity visit(FourthValue value)
    {
        FourthEntity entity = new FourthEntity();
        // Set all properties specific to FourthEntity
        return entity;
    }
}

现在您的外观实现需要AbstractValue,并且您获得了一个您正在寻找的保存方法:

public class FacadeBean implements Facade
{
    @EJB
    private CrudService crudService;

    @Override
    public void save(AbstractValue value)
    {
        AbstractEntity entity = AbstractEntityFactory.create(value);
        crudService.persist(entity);
    }
}

因为您的AbstractValue现在遵循访客模式,所以您可以执行各种多态行为。如:

public class AbstractValuePrinter implements AbstractValueVisitor<Void>
{
    private final Appendable out;

    public AbstractValuePrinter(Appendable out)
    {
        this.out = out;
    }

    private void print(String s)
    {
        try
        {
            out.append(s);
            out.append('\n');
        }
        catch (IOException e)
        {
            throw new IllegalStateException(e);
        }
    }

    @Override
    public Void visit(FirstValue value)
    {
        print("I'm a FirstValue!");
        print("Being a FirstValue is groovy!");
        return null;
    }

    @Override
    public Void visit(SecondValue value)
    {
        print("I'm a SecondValue!");
        print("Being a SecondValue is awesome!");
        return null;
    }

    @Override
    public Void visit(ThirdValue value)
    {
        print("I'm a ThirdValue!");
        print("Meh.");
        return null;
    }

    @Override
    public Void visit(FourthValue value)
    {
        print("I'm a ThirdValue!");
        print("Derp.");
        return null;
    }
}

在此示例中,此访问者不会返回任何内容...它正在做&#34;做&#34;因此,我们只需将返回值设置为Void,因为它不可实例化。然后您只需打印该值:

// (value must not be null)
value.accept(new AbstractValuePrinter(System.out));

最后,访问者模式中最酷的部分(在我看来):你添加FifthValue。您将新方法添加到访问者界面:

T visit(FifthValue value);

突然,你无法编译。您必须解决在两个地方缺少此处理的问题:AbstractEntityFactoryAbstractValuePrinter。这很棒,因为你应该在那些地方考虑它。进行课堂比较(使用instanceof或rinde的班级到工厂地图的解决方案)很可能会错过&#34;新的值类型,现在你有运行时错误...特别是如果你用这些值类型做100个不同的事情。

Anyhoo,我不想进入这个,但你去了:)

答案 1 :(得分:2)

使用带有绑定类型参数的泛型方法,以避免重复:

public <T extends AbstractValue> T save(T value) {...}

在方法正文中,您可以使用与value相关的所有方法引用参数AbstractValue

备注

  • 由于在此示例中您的save方法似乎已被覆盖,因此您可能还需要更改父类或接口的设计。
  • 您还可以使用泛型类(而不是非必需泛型类中的泛型方法),具体取决于您的用例。

答案 2 :(得分:0)

我认为代码中的一个问题是AbstractAssembler的泛型类型是transform方法的输出,而不是输入。如果您按如下方式进行更改:

public abstract class AbstractAssembler<T extends AbstractValue> {
  protected void transformAbstractValueToAbstractObject(AbstractEntity entity, T value) {
    entity.setUniqueId(value.getUniqueId());
    entity.setNominalAmountValue(value.getNominalAmountValue());
  }

  public abstract AbstractEntity transformToEntity(T value);
}

然后,您可以将FacadeBean更改为以下内容。

public class FacadeBean {  
  @EJB
  private CrudService crudService;

  final Map<Class<?>, AbstractAssembler<?>> knownAssemblers;

  FacadeBean() {
    knownAssemblers = new LinkedHashMap<>();
    knownAssemblers.put(FirstValue.class, new FirstAssembler());
    knownAssemblers.put(SecondValue.class, new SecondAssembler());
    // add more assemblers here
  }

  public <T extends AbstractValue> void save(T value, Class<T> type) {
    @SuppressWarnings("unchecked") // safe cast
    final AbstractAssembler<T> assembler = 
      (AbstractAssembler<T>) knownAssemblers.get(type);

    final AbstractEntity entity = assembler.transformToEntity(value);
    this.crudService.persist(entity);
  }  
}

请注意,我更改了save(..)方法的签名,以便我们拥有需要保存的对象的类型。使用这种类型,我们可以简单地查找应该使用的正确汇编程序。并且因为汇编程序现在在其输入类型上是通用的,我们可以进行安全转换(小心保持地图一致)。

此实现避免了代码重复,因为您只需要一个保存方法。通过更改instanceof的泛型类型并将所有汇编程序存储在映射中,可以防止使用AbstractAssembler运算符。

汇编程序可能如下所示:

public class FirstAssembler extends AbstractAssembler<FirstValue> {
  @Override
  public FirstEntity transformToEntity(FirstValue value) {
    final FirstEntity entity = new FirstEntity();
    // do transformational stuff
    super.transformAbstractValueToAbstractObject(entity, value);
    entity.setFixedRate(value.getFixedRate());
    entity.setStartDate(value.getStartDate());
    return entity;
  }
}   

public class SecondAssembler extends AbstractAssembler<SecondValue> {
  @Override
  public SecondEntity transformToEntity(SecondValue value) {
    final SecondEntity entity = new SecondEntity();
    // do transformational stuff
    super.transformAbstractValueToAbstractObject(entity, value);
    return entity;
  }
}

注意:我不熟悉Java bean,因此如果您想使用@Inject ed汇编程序而不是调用构造函数,则可能需要稍微修改代码直接

答案 3 :(得分:0)

你在这里接近镀金,但你可以做一些减少,特别是>>> ships = [item['name'] for item in data['ships'].values()] >>> ships 9: [u'Viper_MkIV', u'SideWinder', u'Asp', u'Type7', u'SideWinder'] >>> - 检查并调用每个扩展的常用字段设置方法。

 columnA | columnB | columnC    
 ____________________________
| A      |   3     | 2       |
| B      |   1     | 5       |

然后是扩展的汇编程序:

null

如果确实希望单个工厂类处理所有值/实体,我会查看访问者模式,使用访问者界面上的泛型类型参数(以及实体/值)进行增强accept方法根据访问接口返回一个类型。我不会在这里展示一个例子,因为我不认为这是你的理由。

答案 4 :(得分:-1)

从保存这些值的类的角度来看,您可以使用一种保存方法,但仍需要实现三种单独的保存方法。

使用所有三种保存方法实现一个类。例如:

public class ValuePersister {
    @Inject
    private Assembler1 assembler1;
    @Inject
    private Assembler2 assembler2;
    @Inject
    private Assembler3 assembler3;

    public Value1 save(Value1 value1, CrudService crudService) {
        Entity1 entity1 = assembler1.transformToObject(value1);
        crudService.persist(entity1);
        return assembler1.transformToValue(entity1);
    }

    public Value2 save(Value2 value2, CrudService crudService) {
        Entity2 entity2 = assembler2.transformToObject(value2);
        crudService.persist(entity2);
        return assembler2.transformToValue(entity2);
    }

    public Value3 save(Value3 value3, CrudService crudService) {
        Entity3 entity3 = assembler3.transformToObject(value3);
        crudService.persist(entity3);
        return assembler3.transformToValue(entity3);
    }
}

向AbstractValue添加一个抽象方法:

public abstract AbstractValue save(ValuePersister valuePersister, CrudService crudService);

在扩展AbstractValue的每个类中实现该方法:

@Override
public AbstractValue save(ValuePersister valuePersister, CrudService crudService) {
    return valuePersister.save(this, crudService);
}

注入ValuePersister并实现原始的通用保存方法:

@Inject
private ValuePersister valuePersister;

@Override
public AbstractValue save(AbstractValue value) {
    return value.save(valuePersister, crudService)
}