java - Which pattern to use to avoid code duplication with object value transformer -


i want rid of following code duplication within myfacadebean. consider following situation:

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);  } 

with 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);     }  } 

with following assemblers:

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());     }  } 

with following entities:

@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;     }  } 

i tried following approach:

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);     } } 

problems assemblerfactoryimpl , assemblerimpl in have instanceof checks , castings...

another idea let value know transformer use (or how transform). want value "dumb".

@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; } 

does not work, because of

type mismatch: cannot convert firstassembler abstractassembler

i'm posting separate answer, since don't think there's wrong having save method every abstractvalue type.

first we'll establish base value class example. i'm using interface don't muddy waters. abstractvalue interface:

public interface abstractvalue {     int getuniqueid();     double getnominalvalue();     <t> t accept(abstractvaluevisitor<t> visitor); } 

and "visitor interface":

public interface abstractvaluevisitor<t> {     t visit(firstvalue value);     t visit(secondvalue value);     t visit(thirdvalue value);     t visit(fourthvalue value); } 

i know don't want intelligence baked abstractvalue, going add one specification... concrete implementations of abstractvalue (all four) implement accept method way:

@override public <t> t accept(abstractvaluevisitor<t> visitor) {     return visitor.visit(this); } 

so method implemented four times: in 4 value classes, same way. because visitor interface aware of concrete implementations, appropriate method called each particular value type. 3 of these parts put "visitor pattern".

now we'll make entity factory. job create appropriate abstractentity when provided 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 properties specific firstentity         entity.setfixedrate(value.getfixedrate());         entity.setstartdate(value.getstartdate());         return entity;     }      @override     public abstractentity visit(secondvalue value)     {         secondentity entity = new secondentity();         // set properties specific secondentity         entity.settransactiontype(value.gettransactiontype());         entity.setvaluedate(value.getvaluedate());         return entity;     }      @override     public abstractentity visit(thirdvalue value)     {         thirdentity entity = new thirdentity();         // set properties specific thirdentity         return entity;     }      @override     public abstractentity visit(fourthvalue value)     {         fourthentity entity = new fourthentity();         // set properties specific fourthentity         return entity;     } } 

now facade implementation takes abstractvalue, , got 1 save method you're looking for:

public class facadebean implements facade {     @ejb     private crudservice crudservice;      @override     public void save(abstractvalue value)     {         abstractentity entity = abstractentityfactory.create(value);         crudservice.persist(entity);     } } 

because abstractvalue follows visitor pattern, can sorts of polymorphic behavior. such as:

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 firstvalue!");         print("being firstvalue groovy!");         return null;     }      @override     public void visit(secondvalue value)     {         print("i'm secondvalue!");         print("being secondvalue awesome!");         return null;     }      @override     public void visit(thirdvalue value)     {         print("i'm thirdvalue!");         print("meh.");         return null;     }      @override     public void visit(fourthvalue value)     {         print("i'm thirdvalue!");         print("derp.");         return null;     } } 

in example, visitor isn't returning anything... it's "doing" something, we'll set return value void, since it's non-instantiatable. print value simply:

// (value must not null) value.accept(new abstractvalueprinter(system.out)); 

finally, coolest part of visitor pattern (in opinion): add fifthvalue. add new method visitor interface:

t visit(fifthvalue value); 

and suddenly, you can't compile. must address lack of handling in 2 places: abstractentityfactory , abstractvalueprinter. great, because should consider in places. doing class comparisons (with either instanceof or rinde's solution of class-to-factory map) "miss" new value type, , have runtime bugs... if you're doing 100 different things these value types.

anyhoo, didn't want this, there go :)


Comments

Popular posts from this blog

authentication - Mongodb revoke acccess to connect test database -

r - Update two sets of radiobuttons reactively - shiny -

ios - Realm over CoreData should I use NSFetchedResultController or a Dictionary? -