I need to modify the data members of a class, but since there are a lot of use cases, I would like to put the modifier methods in separate classes to not make the main class too large.
So I'm thinking of making the data members protected, and using derived classes as interfaces to modify the data members for each different use case.
Is this a common solution to my problem? Is this a good way of solving it, or is there something better?
-
What other types of methods are in the class making it "too large"?user949300– user94930010/03/2017 15:11:31Commented Oct 3, 2017 at 15:11
-
@user949300 Methods for all use cases. Not counting the use cases that will modify the data members, the class will only have getters and a constructor.devil0150– devil015010/03/2017 18:13:01Commented Oct 3, 2017 at 18:13
-
Sounds like your concern is that you have "a lot of use cases", bordering on too many for this class. This is a possible indication that your class is trying to do too much and violates the Single Responsibility Principle. Unfortunately, the SRP is ill-defined, very poorly understood, and discussion of it usually leads to more heat than light. Without knowing more of your project, it's hard to say, but you should consider breaking your use-cases into smaller groups, and seeing if that leads to a new class design with more, but smaller, classes.user949300– user94930010/04/2017 19:11:02Commented Oct 4, 2017 at 19:11
1 Answer 1
I need to modify the data members of a class, but since there are a lot of use cases, I would like to put the modifier methods in separate classes to not make the main class too large.
I think it's a good idea to extract the modification logic outside the class that defines the members. It sounds like your data members are a model and the modification logic is probably business logic. Please correct me if I'm wrong.
So I'm thinking of making the data members protected, and using derived classes as interfaces to modify the data members for each different use case. Is this a common solution to my problem? Is this a good way of solving it?
I've never seen something like this before, and it doesn't sound like good design to me. Here are the reasons:
- It's an unusual approach to take, and it may confuse your colleagues, and those who need to implement a new use case may not understand how.
- Inheritance is less desirable than composition
- Mutability is less desirable than immutability
is there something better?
I would take a different approach. I would make the model class immutable:
class Foo {
private final Integer a;
private final String b;
public Foo(Integer a, String b) {
this.a = a;
this.b = b;
}
public Integer getA() { return a; }
public String getB() { return b; }
}
Now that I have a model, I want to transform it. I can define a model transformation:
interface FooTransformation {
Foo transform(Foo foo);
}
You can now express all your use cases in terms of FooTransformation
implementations like so:
class UseCase1 implements FooTransformation {
public Foo transform(Foo foo) {
return new Foo(foo.getA() + 5, foo.getB() + "world");
}
}
This design has some good things:
- Immutable model
- Easy to unit test use cases
-
1Currently this is only the beginning of the project, but I think performance will be important in later stages. Instantiating a new object for every modification seems a little heavy.devil0150– devil015010/02/2017 22:35:54Commented Oct 2, 2017 at 22:35
-
1Would be a nice addition to mention how mutating a supertype's protected state via the subtype would violate the history constraint of LSP: "Because subtypes may introduce methods that are not present in the supertype, the introduction of these methods may allow state changes in the subtype that are not permissible in the supertype. The history constraint prohibits this."Dioxin– Dioxin10/05/2017 23:45:23Commented Oct 5, 2017 at 23:45