We've implemented the adapter design pattern whose job is the following:
Act as a liaison between service and data access layers.
Convert raw data (from data source, internal or external) to domain specific data. Do necessary validation and massaging.
Sometimes, making the DAO calls may depend on data not readily available from input parameters or additional service calls may need to be made based on input data. In other words, the adapter can't always do a 1:1 mapping between the service and the DAO. It may map the same call from service to different DAO calls based on the input parameters.
Item #3 is starting to worry me as the adapters are becoming more complicated than I'd originally imagined. I'm not aware of a design pattern to trim down an adapter. Is there one? Suggestions?
You've used what I like to call the "swiss army knife" pattern.
Point 1 is broker pattern (or similar)
Point 2 is adapter pattern (or similar)
Point 3 is content based routing (or similar)
Best practice says you should break up your class into at least 3 classes, one for each concern.
Instead of using an adapter or a full Repository(CRUD operations), i would use an IReader interface for reading and visitor pattern for insert update delete, so you can separate domain logic from infraestructure(persistance) details, Here is the idea:
public class MyBusinessObject : IAcceptBusinessVisitor, IAcceptMyBusinessIdVisitor
{
private readonly string _id;
private string MyPrivateProp { get; set; }
//Fully encapsulated object
public MyBusinessObject(string id, string myPrivateProp)
{
_id = id;
MyPrivateProp = myPrivateProp;
}
public void UpdateMyProp(string newProp)
{
if (string.IsNullOrWhiteSpace(newProp)) throw new ArgumentNullException(nameof(newProp));
//Business rules ...
MyPrivateProp = newProp;
}
public void Accept(IMyBusinessObjectVisitor visitor)
{
if (visitor == null) throw new ArgumentNullException(nameof(visitor));
visitor.Visit(_id, MyPrivateProp);
}
public void Accept(IMyBusinessIdVisitor visitor)
{
if (visitor == null) throw new ArgumentNullException(nameof(visitor));
visitor.Visit(_id);
}
}
public interface IAcceptBusinessVisitor
{
void Accept(IMyBusinessObjectVisitor visitor);
}
public interface IAcceptMyBusinessIdVisitor
{
void Accept(IMyBusinessIdVisitor visitor);
}
public interface IMyBusinessObjectVisitor
{
void Visit(string id, string prop);
}
public interface IMyBusinessIdVisitor
{
void Visit(string id);
}
public class SavePersistanceVitor : IMyBusinessObjectVisitor
{
public void Visit(string id, string prop)
{
//Save to Database
}
}
public class UpdatePersistanceVitor : IMyBusinessObjectVisitor
{
public void Visit(string id, string prop)
{
//Update to Database
}
}
public class DeleteVitor : IMyBusinessIdVisitor
{
public void Visit(string id)
{
//Delete in Database
}
}
Here for Reading:
public interface IMyBusinessObjectReader
{
MyBusinessObject Read(string id);
}
class MyBusinessObjectReaderFromDb : IMyBusinessObjectReader
{
public MyBusinessObject Read(string id)
{
//Read from database
string myPrivateProp = "";
return new MyBusinessObject(id, myPrivateProp);
}
}
the next step could be adding generics for reading and the visitors. In this case you end up having little tiny classes and gain flexibility and the benefits of solid principles like single responsability, interface segregation, etc. So you can create a rich encapsulated domain and extend its functionality with some desing principles.
Regards!
Related
I have entity in database, say, MonthPlan:
class MonthPlan {
private boolean approved;
// other fields
}
There is also REST interface, which accepts external requests based on which program changes entity instances. For example, request
class EditMonthPlanRequest {
private long amount;
// other fields
}
is used to change month plan amount.
What I need is to execute different actions on MonthPlan entity based on value of approved field. For example, code for mentioned request could be as following
MonthPlan plan = getPlan(...);
if (plan.isApproved()) {
// actions using data from EditMonthPlanRequest
} else {
// other actions using data from EditMonthPlanRequest
}
There would be 5-6 different requests each with exactly two variants of actions based on value of approved field of edited entity. What OOP design pattern can I use for such use case to write more concise code?
I do not think you need a design pattern in such a simple case. Each request will be processed by the corresponding method at Service layer.
In this scenario, the state pattern is more suitable.
State design pattern is used when an Object changes its behavior based on its internal state.
If we have to change behavior of an object based on its state, we can have a state variable in the Object and use if-else condition block to perform different actions based on the state. State pattern is used to provide a systematic and lose-coupled way to achieve this through Context and State implementations.
Try to implement based on your description:
public class StatePattern {
public static void main(String[] args) {
MonthPlan monthPlan = null; //= new MonthPlan(...)
StateContext stateContext = new StateContext();
if(monthPlan.isApproved()) {
stateContext.setState(new Approved());
}else {
stateContext.setState(new NotApproved());
}
}
}
class MonthPlan {
private boolean approved;
public boolean isApproved() {
return approved;
}
// other fields
}
interface State{
public void doAction(StateContext ctx);
}
class StateContext{
private State currentState;
public StateContext() {
//default Approved state, you can change if you want
currentState = new Approved();
}
public void setState(State state) {
currentState = state;
}
public void doAction() {
currentState.doAction(this);
}
}
class Approved implements State{
#Override
public void doAction(StateContext ctx) {
//actions using data from EditMonthPlanRequest
}
}
class NotApproved implements State{
#Override
public void doAction(StateContext ctx) {
//other actions using data from EditMonthPlanRequest
}
}
For this simple case, the Template Method pattern may apply:
abstract class AbstractRequest {
public void execute(...){
MonthPlan plan = getPlan(...);
if (plan.isApproved()) {
executeForApproved(plan);
} else {
executeForNonApproved(plan);
}
}
protected abstract void executeForApproved(MonthPlan plan);
protected abstract void executeForNonApproved(MonthPlan plan);
}
This way, you don't need to repeat the if statement and the getPlan(...) in each subclass:
class EditMonthPlanRequest extends AbstractRequest {
private long amount;
// other fields
protected void executeForApproved(MonthPlan plan){
...
}
protected void executeForNonApproved(MonthPlan plan){
...
}
}
If you want to do OOP, then replace conditionals with polymorphism.
In this example, it means splitting MonthPlan in two.
class ApprovedMonthPlan extends MonthPlan
class UnapprovedMonthPlan extends MonthPlan
Each class handles EditMonthPlanRequest in its own way.
I have two ways of saving data in my application: save to database and save to file. Since I don't want client code dealing with construction of objects I created a class that (to my understanding) is simple factory with a factory method. Code below:
public static DataPersister createDataPersister(Boolean saveToDb, Session session, String filename) {
if (saveToDb) {
return new DatabaseDataPersister(session);
} else {
return new FileDataPersister(filename);
}
}
With this setup client code doesn't have to deal with constructing anything or deciding whether to save to DB or file - it can just call a save() method on an object returned by the factory like so:
DataPersister dataPersister = DataPersisterSimpleFactory.createDataPersister(this.savetoDb, this.session, this.filename);
dataPersister.save(this.data);
My question is - is this solution breaking SOLID principles? In order to create e.g. a DatabaseDataPersister client code needs to pass on a filename parameter, and this implementation of DataPersister won't have any use of it. I feel like it doesn't sit right with something similar to Interface-segregation principle but not quite that.
And if the solution is indeed a code smell - how do I go about cleaning it?
The SOLID principle I think is in violation is DIP.
Your client classes, by having to depend on the static factory directly, have a compile-time dependency on actual implementations, DatabaseDataPersister and FileDataPersister, rather than just the abstraction DataPersister.
To solve, supply to the client the DataPersister you want them to use. The constructor is usually a good place for this:
public class ExampleClient {
private final DataPersister dataPersister;
public ExampleClient(DataPersister dataPersister) {
this.dataPersister = dataPersister;
}
public void methodThatUsesSave(){
dataPersister.save(data);
}
}
This code compiles without the concrete implementations, i.e. it has no dependency on them. The client also doesn't need to know the filename or session so it solves that code smell too.
We can decide which concrete implementation to give it at construction time, here I use your existing method:
DataPersister dataPersister = DataPersisterSimpleFactory.createDataPersister(this.savetoDb, this.session, this.filename);
ExampleClient example = new ExampleClient(dataPersister);
This is a perfect opportunity to use the factory pattern
interface DataPersister {
void persist(String s);
}
private class DatabasePersister implements DataPersister {
final Session session;
public DatabasePersister(Session session) {
this.session = session;
}
#Override
public void persist(String s) {
System.out.println("Persist to database: " + s);
}
}
private class FilePersister implements DataPersister {
final String filename;
public FilePersister(String filename) {
this.filename = filename;
}
#Override
public void persist(String s) {
System.out.println("Persist to file: " + s);
}
}
class PersisterFactory {
public DataPersister createDatabasePersister(Session session) {
return new DatabasePersister(session);
}
public DataPersister createFilePersister(String filename) {
return new FilePersister(filename);
}
}
public void test(String[] args) {
DataPersister databasePersister = new PersisterFactory().createDatabasePersister(new Session());
databasePersister.persist("Hello");
DataPersister filePersister = new PersisterFactory().createFilePersister("Hello");
filePersister.persist("Hello");
}
You already pass a bunch of stuff irrelevant to various persisters.
As it stands you need a method that takes a Session and one that takes a String and you're done. No need for a boolean, no need for useless params. That handles your decision making with no cruft.
Whether or not that's a good idea... I'm ambivalent. You're not saving much; might as well just have a static factory in each type so it's explicit in the code what type you're creating.
Consider what happens when you add a new persister, like a REST endpoint, that would take a URL (could be a string, could be an actual URL). You now need even more useless parameters etc. Or you could pass in a URI from the beginning, e.g., file:// or http:// and get around that problem.
There are any number of ways this could be done–I'm not convinced there's a "clearly correct" answer, and it may boil down to opinion.
Well the right solution here is combining the dependency injection from weston and the factory pattern from OldCurmudgeon.
public class ExampleClient {
private final DataPersister dataPersister;
public ExampleClient(DataPersister dataPersister) {
this.dataPersister = dataPersister;
}
public void methodThatUsesSave(){
dataPersister.save(data);
}
}
class PersisterFactory {
public DataPersister createDatabasePersister(Session session) {
return new DatabasePersister(session);
}
public DataPersister createFilePersister(String filename) {
return new FilePersister(filename);
}
}
The upper level code:
PersisterFactory = new PersisterFactory();
DataPersister dataPersister;
if (saveToDb)
dataPersister = PersisterFactory.createDatabasePersister(new Session());
else
dataPersister = PersisterFactory.createFilePersister("Hello");
ExampleClient example = new ExampleClient(dataPersister);
Usually the dataPersister comes from the DI container and the saveToDb comes from the config, but of course testing can be an exception.
I have an interface and its 2 implementations say :
public interface ObjectProcessor {
public void process(List<String> objectNames);
}
public CarImpl implements ObjectProcessor {
#override
public void process(List<String> carNames){
//car logic
} }
public VanImpl implements ObjectProcessor {
#override
public void process(List<String> vanNames){
//van logic
}
}
Now the caller who uses this interface looks like :
public void caller(VehicleType vehicleType, List<String> vehicleNames ) {
ObjectProcessor processor = null ;
if (VehicleType == VehicleType.CAR) {
processor = new CarImpl();
processor.process(vehicleNames);
}
}
VehicleType being an ENUM
This works fine. But is there anyway I can call an interface dynamically without
adding if statements. In the future if I am supporting another vehicle , I need to add an if statement along with a new implementation for the interface . How can I avoid this?
Overwrite abstract factory method in enum like this.
public enum VehicleType {
Car {
#Override
public ObjectProcessor createImpl() {
return new CarImpl();
}
},
Van {
#Override
public ObjectProcessor createImpl() {
return new VanImpl();
}
};
public abstract ObjectProcessor createImpl();
}
public void caller(VehicleType vehicleType, List<String> vehicleNames ) {
ObjectProcessor processor = vehicleType.createImpl();
processor.process(vehicleNames);
}
VechicleType combines enumeration with factory.
Or you can wirte all logics in enum like this.
public enum VehicleType {
Car {
#Override
public ObjectProcessor createImpl() {
return new ObjectProcessor() {
#Override
public void process(List<String> objectNames) {
// car logic
}
};
}
},
Van {
#Override
public ObjectProcessor createImpl() {
return new ObjectProcessor() {
#Override
public void process(List<String> objectNames) {
// van logic
}
};
}
};
public abstract ObjectProcessor createImpl();
}
In this case you don't need implementation classes (CarImpl, VanImpl, ...) any more.
Use Factory pattern. Here are some benefit from using it: http://javarevisited.blogspot.com/2011/12/factory-design-pattern-java-example.html#ixzz3ueUdV947
1) Factory method design pattern decouples the calling class from the target class, which result in less coupled and highly cohesive code?
2) Factory pattern in Java enables the subclasses to provide extended version of an object, because creating an object inside factory is more flexible than creating an object directly in the client. Since client is working on interface level any time you can enhance the implementation and return from Factory.
3) Another benefit of using Factory design pattern in Java is that it encourages consistency in Code since every time object is created using Factory rather than using different constructor at different client side.
4) Code written using Factory design pattern in Java is also easy to debug and troubleshoot because you have a centralized method for object creation and every client is getting object from same place
What you're basically implementing is a Factory pattern like proposed in the other answers. But in the end you will have to write an 'if' or 'switch' statement to select to correct implementation (or strategy) for your enum value. But like you mentioned yourself you'd have to extend this selection pattern whenever you add or remove an enum value. You can circumvent this by using a map like so:
public class ProcessorSelector {
private final Map<VehicleType, ObjectProcessor> processors;
public ProcessorSelector(Map<VehicleType, ObjectProcessor> processors) {
this.processors = processors;
}
public void process(VehicleType type, List<String> input) {
processors.get(type).process(input);
}
}
You can than configure your ProcessorSelector by passing a map with all the processor implementations mapped to the correct enum value (notice I used guava's ImmutableMap to conveniently construct the hashmap:
new ProcessorSelector(ImmutableMap.of(
VehicleType.CAR, new CarImpl(),
VehicleType.VAN, new VanImpl());
You'll never have to change your ProcessorSelector again, only the construction/configuration of the class. In fact you could say we just implemented the strategy pattern here. These selector classes are very common and if you feel you are implementing them quite often you could even use a more generic implementation, I recently described this in a blogpost: https://hansnuttin.wordpress.com/2015/12/03/functionselector/
At my work, we have surveys, and one survey involves multiple steps. I work in automation, so I design tests around the page-objects we create for these surveys. We call this particular survey a "flow" survey because it has multiple steps. So you can skip step1 (survey A), then complete or skip step 2 (survey B), then complete or skip step 3 (survey C). Naively, we could write a test that just has methods that look like this:
public void completeSurveyA() {
//...
}
public void skipSurveyB() {
//...
}
public void completeSurveyB() {
//...
}
public void skipSurveyC() {
//...
}
public void completeSurveyC() {
//...
}
You would use it like this
completeSurveyA();
skipSurveyB();
completeSurveyC();
However, that could be a problem because we might call completeSurveyB() before we call completeSurveyA(), call completeSurveyA twice, etc. and the test would break. To avoid this, I introduced a different approach where calling a method on surveyA would return a surveyB object, which would return a surveyC object.
public class SurveyFlow() {
public SurveyB completeSurveyA() {
//...
return new SurveyB();
}
private class SurveyB() {
public SurveyC skipSurveyB() {
//...
return new SurveyC();
}
public SurveyC completeSurveyB() {
//...
return new SurveyC();
}
private class SurveyC() {
public void skipSurveyC() {
//...
}
public void completeSurveyC() {
//...
}
}
}
}
You would use it like this
new SurveyFlow().completeSurveyA().skipSurveryB().completeSurveyC();
The pattern reminds me of a state machine because only certain methods are available to you in different states, but I'm wondering if there is a more specific name for this pattern.
According to the classes of your example, it's a FluentInterface:
Probably the most important thing to notice about this style is that the intent is to do something along the lines of an internal DomainSpecificLanguage. (...) The API is primarily designed to be readable and to flow.
It's not the builder pattern, because you're not building anything (i.e. you don't have a final build() method where data gathered in previous steps is used to create an instance).
It's not the state pattern either, because operations (skip() and complete() in this case) do not depend on the state of an object (actually steps don't have a state).
It would have been the state pattern if the whole survey had been modeled as an object with one method whose implementation depended on different states (in this case, the states would be the steps plus the action taken, i.e. surveyACompleted, surveyASkipped, surveyBCompleted, surveyBSkipped, etc, while the method would be something like nextStep()):
public class SurveyFlow {
private SurveyState state; // this represents the current step
public SurveyFlow(boolean skipFirst) {
this.state = skipFirst ? new SurveyASkipped() : new SurveyACompleted();
}
void setState(SurveyState state) {
this.state = state;
}
public void takeStep(boolean skipNext) { // takeStep operation delegated
// to the state (current step)
this.state.takeStep(skipNext, this); // "this" passed to the step so
// that it can switch to the
// next step if needed
}
}
The state would be polymorphically represented by each step of the SurveyFlow:
abstract class SurveyState {
protected abstract void takeStep(boolean skipNext, SurveyFlow survey);
}
Survey A states would be as follows:
class SurveyACompleted extends SurveyState {
protected void takeStep(boolean skipNext, SurveyFlow survey) {
// ...
survey.setState(skipNext ? new SurveyBSkipped() : new SurveyBCompleted());
}
}
class SurveyASkipped extends SurveyState {
protected void takeStep(boolean skipNext, SurveyFlow survey) {
// ...
survey.setState(skipNext ? new SurveyBSkipped() : new SurveyBCompleted());
}
}
Survey B states would be as follows:
class SurveyBCompleted extends SurveyState {
protected void takeStep(boolean skipNext, SurveyFlow survey) {
// ...
survey.setState(skipNext ? new SurveyCSkipped() : new SurveyCCompleted());
}
}
class SurveyBSkipped extends SurveyState {
protected void takeStep(boolean skipNext, SurveyFlow survey) {
// ...
survey.setState(skipNext ? new SurveyCSkipped() : new SurveyCCompleted());
}
}
For your example:
Complete Survey A
Skip Survey B
Complete Survey C
You could do:
SurveyFlow survey = new SurveyFlow(false); // will complete survey A
survey.takeStep(true); // completed survey A and will skip survey B
survey.takeStep(false); // skipped survey A and will complete survey C
survey.takeStep(true); // completed survey C
If survey C is the last step, then it can ignore the boolean argument and shouldn't set further steps.
This is in a way the State pattern, but does not completely adhere to the State pattern described by GoF, because you are not changing the state of a single object, but rather creating and returning a new object of different class which you use afterwards.
Actually, this resembles much more the Builder pattern, where the completeSurveyC() acts as a build or getResult method to build a Surway from multiple consisting pieces specified earlier.
I have been looking over a couple of classes I have in an android project, and I realized that I have been mixing logic with data. Having realized how bad this can be to the readability and the test-ability of my project, I decided to do some refactoring in order to abstract away all services logic to separate services modules. However, since I have been relying on Java's polymorphism, I got lost and need some guidance.
Suppose I have this "to-be-changed" layout for a super data class, and two sub-classes:
public class DataItem {
/* some variables */
public saveToDB(/* Some Arguments */) {
/* do some stuff */
}
public render() {
/* render the class */
}
}
public class ChildDataItemA extends DataItem {
#Override
public saveToDB(/* Some Arguments */) {
super.saveToDB();
/* more specific logic to ChildDataItemA */
}
#Override
public render() {
/* render logic for ChildDataItemA */
}
}
public class ChildDataItemB extends DataItem {
#Override
public saveToDB(/* Some Arguments */) {
super.saveToDB();
/* more specific logic to ChildDataItemB */
}
#Override
public render() {
/* render logic for ChildDataItemB */
}
}
Now, I thought about moving the saveToDB() and render() methods to a service class. However, sometimes I need to be able to call these method into instance of compiled type DataItem without knowing its runtime type. For instance, I might want to make the following call:
List<DataItem> dataList;
for (DataItem item: dataList) {
item.saveToDB();
item.render();
}
Additionally, I thought of doing the following:
public class ChildDataItemB extends DataItem {
#Override
public saveToDB(/* Some Arguments */) {
super.saveToDB();
/* more specific logic to ChildDataItemB */
Service.saveToDBB();
}
#Override
public render() {
/* render logic for ChildDataItemB */
Service.renderB();
}
}
Where I still keep 'dummy' methods in each subclass that would call an appropriate service method. However, I do not think that this really achieves the separation I want since data classes will still know about services (bad!).
Any ideas on how to solve this?
Edit: Note that render() and saveToDB() are just generic examples of what these methods can be, so the problem is not really about choosing an ORM or SQL related techniques.
Visitor pattern to the rescue. Create a visitor interface and have each service implement this interface:
public interface DataItemVisitor {
// one method for each subtype you want to handle
void process(ChildDataItemA item);
void process(ChildDataItemB item);
}
public class PersistenceService implements DataItemVisitor { ... }
public class RenderService implements DataItemVisitor { ... }
Then have each DataItem implement an accept method:
public abstract class DataItem {
public abstract void accept(DataItemVisitor visitor);
}
public class ChildDataItemA extends DataItem {
#Override
public void accept(DataItemVisitor visitor) {
visitor.process(this);
}
}
public class ChildDataItemB extends DataItem {
#Override
public void accept(DataItemVisitor visitor) {
visitor.process(this);
}
}
Note that all accept implementations look the same but this refers to the correct type in each subclass. Now you can add new services without having to change the DataItem classes.
So you want to do:
List<DataItem> dataList;
for (DataItem item: dataList) {
service.saveToDB(item);
service.render(item);
}
For this you need to setup a system for your service to know more details from your DataItem subclass.
ORM's and serializers usually solve this via a metadata system, e.g. by finding an xml file with name matching the subclass, containing the properties to save or serialize.
ChildDataItemA.xml
<metaData>
<column name="..." property="..."/>
</metaData>
You could get the same result via reflection and annotations.
In your case, an application of the Bridge pattern could also work:
class DataItem {
public describeTo(MetaData metaData){
...
}
}
class Service {
public void saveToDB(DataItem item) {
MetaData metaData = new MetaData();
item.describeTo(metaData);
...
}
}
Your metadata could be decoupled from saving or rendering, so you can the same for both.
I would clean the "data" classes of render and saveToDB methods.
Instead, I would create a hierarchy of wrappers for DataItem (it does not have to mimic exactly the DataItem hierarchy). These wrappers will be the ones implementing those methods.
Additionally, I suggest that (if you can), you move to some ORM (Object-Relational Mapping) like Hibernate or JPA to get rid of the saveToDB method.
First of all the DataItem class should be clean, only with getters and setter and no logic at all, just like a POJO. moreover- your DataItem maybe should be abstract.
Now- for the logic, like others suggested I would use some ORM framework for the saveToDB part, but you said that it's not helping you cause it's android project and you have other methods like this as well.
So what I would do is to create an interface- IDataItemDAO, with the following logic:
public interface IDataItemDAO<T extends DataItem > {
public void saveToDB(T data, /* Some Arguments */);
... other methods that you need ...
}
I would create an abstract DAO for the DataItem and put it all the similar code of all DataItems:
public abstract class ChildDataItemADAO impelemets IDataItemDAO<DataItem> {
#Override
public void saveToDB(DataItem data, /* Some Arguments */); {
...
}
}
than I would create a DAO for each DataItem class that you have:
public class ChildDataItemADAO extends DataItemDAO impelemets IDataItemDAO<ChildDataItemA> {
#Override
public void saveToDB(ChildDataItemA data, /* Some Arguments */); {
super(data, ...);
//other specific saving
}
}
the other part is how to use the correct DAO for the correct instance, for this I would create a class that will bring me the correct DAO for the given instance, it is a very simple method if an if-else statements (or you can do it dynamically with a map of class and the DAO)
public DataItemDAO getDao(DataItem item) {
if (item instanceof ChildDataItemA) {
//save the instance ofcourse
return new ChildDataItemADAO();
}
}
so you should use it like this:
List<DataItem> dataList;
for (DataItem item: dataList) {
factory.getDao(item).saveToDB(item);
}
If you want separate logic from data you may try the following approach
Create your data class DataItem,ChildDataItemA, ChildDataItemB without the method operating on the data
Create an interface for some operations on you data class something like
public interface OperationGroup1OnDataItem {
public void saveToDB(DataItem dataItem/*plus other params*/) {
}
public void render(DataItem dataItem/*plus other params*/) {
}
......
}
Create a factory for implementing an OperationGroup provider
public class OperationFactoryProvider {
public static OperationGroup1OnDataItem getOperationGroup1For(Class class) {
....
}
}
Use it in you code:
List<DataItem> dataList;
for (DataItem item: dataList) {
OperationGroup1OnDataItem provider OperationFactoryProvider.getOperationGroup1For(item.class);
provider.saveToDB(item);
provider.render(item);
}
You can choose to implement the factory with a simple static map where you put the class (or the class fullName) as the key and an Object implementing the interface as the value; something like
Map<String,OperationGroup1OnDataItem> factoryMap= new HashMap<String,OperationGroup1OnDataItem>();
factoryMap.put(DataItem.class.getName(),new SomeClassThatImplementsOperationGroup1OnDataItemForDataItem());
factoryMap.put(ChildDataItemA.class.getName(),new SomeClassThatImplementsOperationGroup1OnDataItemForChildDataItemA());
The implementation of the getOperationGroup1For is:
return factoryMap.get(item.getClass().getName());
This is one example of separating logic from data, if you want separate logic from data your logic methods must be extracted from your data class; otherwise there is no separation. So I think every solution must start from removing logic methods.