Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Status:
Answers of Fendy and Glen Best are equally acceptable and honored by me but since one can be accepted and bounty be given, I choose Fendy's answer.
Scenario:
If I have some code that has to be reused many times in many classes (rarely with minor parameter changes which is obvious) and concurrent threads, Which approach to go for?
The code that has to be reused can be any sane thing (with appropriate care of static and non-static context in mind and method making techniques). It can be an algorithm, A DB method doing connect,operate,close. Anything.
Make some class like MyMethods.class
and put all those methods in it.
1.a. Make methods static
and call (by all classes and concurrent threads) directly as MyMethods.someMethod();
1.b. Make methods non-static
and at the time to call them, instantiate
the whole class by MyMethods mm = MyMethods(); mm.someMethod();
Use Strategy pattern stated at https://en.wikipedia.org/wiki/Strategy_pattern (code attached here with).
Use dependency injection stated at https://en.wikipedia.org/wiki/Dependency_injection#Java
Problems:
Some people would say Unit test http://en.wikipedia.org/wiki/Unit_testing wont be possible with this approach, will make trouble in swaping out latter. if you want to test your class and use a mock version of the dependency
1.a. Will there be any problems with concurrent calls or multiple classes? specially in JDBC static methods
for just an example?
1.b. I think it would make too much memory load as a whole class would be instanticated
many times just to call one or two methods
Thats way over my head, do explain that and or any advantages/disadvantages
I do Not want to use a framework in context to this question.. Thats way over my head, do explain that and or any advantages/disadvantages
Awaiting any other strategies or recommendations, if any.
Request:
Please only answer if you are experienced and know the implications deeply and can comprehensively, with your answer, help me and the community as a whole!
Code:
/** The classes that implement a concrete strategy should implement this.
* The Context class uses this to call the concrete strategy. */
interface Strategy {
int execute(int a, int b);
}
/** Implements the algorithm using the strategy interface */
class Add implements Strategy {
public int execute(int a, int b) {
System.out.println("Called Add's execute()");
return a + b; // Do an addition with a and b
}
}
class Subtract implements Strategy {
public int execute(int a, int b) {
System.out.println("Called Subtract's execute()");
return a - b; // Do a subtraction with a and b
}
}
class Multiply implements Strategy {
public int execute(int a, int b) {
System.out.println("Called Multiply's execute()");
return a * b; // Do a multiplication with a and b
}
}
// Configured with a ConcreteStrategy object and maintains
// a reference to a Strategy object
class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int a, int b) {
return this.strategy.execute(a, b);
}
}
/** Tests the pattern */
class StrategyExample {
public static void main(String[] args) {
Context context;
// Three contexts following different strategies
context = new Context(new Add());
int resultA = context.executeStrategy(3,4);
context = new Context(new Subtract());
int resultB = context.executeStrategy(3,4);
context = new Context(new Multiply());
int resultC = context.executeStrategy(3,4);
System.out.println("Result A : " + resultA );
System.out.println("Result B : " + resultB );
System.out.println("Result C : " + resultC );
}
}
Your question actually has two meanings.
that has to be reused many times in many classes
It can be a context of design pattern (reusable component) or memory cost (class instantiation). Talking from two different perspective:
Memory Cost (I had little experience on this, but let me share my experience)
This section actually only cover 2 kind of instantiation.
First is static (or DI instantiation in composition root)
- Eager instantiation, means all class will be instantiated when application start
- One time instantiation only
Non-static
- Lazy instantiation, means class will only be instantiated when needed
- One time instantiation every use
In short, static will cost high if the class is many, and non-static will cost high if the request is high (inside for loop, for example). But it should not make your application heavy. Most operation in java / csharp are creates objects though.
Class Reusability
1 - mega monolithic code (one god class able to do almost everything)
Advantages:
- Easy to search for code (still depends though), you know that every logic lies there so you just need to look at that big class
- If it is static, you can just call it anywhere without worrying about the instantiation
Disadvantages:
- Any modification for one method creates risk for error in other places
- Violates SRP, means this class can be changed by various reason, not only one
- Especially in versioning, it is harder to merge if modification happen in separated branches, resulting effort in synchronize code
1a / static class / singleton pattern
Advantages:
- Easy to use
- Can be used anywhere (just reference and call, and done)
- Not need to instantiate object
Disadvantages:
- Hard to unit test (it is hard to mock, and at latter time, you will find it is taking time to prepare the test environment. Especially with data
- If stateful (has state), it is hard to determine current state, during debugging. Moreover, it is hard to determine which function changes the state, in can be changed from everywhere
- Tend to have much parameters (maybe around 5-11)
Some point about static class: see this question
2 strategy pattern
Actually this has the same design with 3 or composition over inheritance
.
3 dependency injection
Advantages:
- Easy to mock and unit test
Must be stateless
. It is easier to debug and unit test if the class is stateless
- Support to be refactored
The disadvantage:
- Hard to debug for those who does not familiar with interfaces (everytime you redirect to the method, it goes to interface's)
- Creates layering which will resulting to mapping
State / Stateless
I think states plays important rules in your application design. Usually developers try to avoid having states in business logic code, such as:
// get data
if(request.IsDraft){
// step 1
// step 2
}
else{
// step 1
// step 3
}
Developers tend to put the logic in other stateless
class, or at least methods such as:
// get data
if(request.IsDraft){
draftRequestHandler.Modify(request);
}
else{
publishedRequestHandler.Modify(request);
}
It will provide in better readability, and easier for modification and unit tests. There is one design pattern state pattern or hierarchial state machine pattern
too, especially to handle some cases like this.
Single Responsibility Principle
IMHO, this principle is has the most benefit if followed. The advantages are:
- In versioning, the changes are clear about which class has been modified and why
- In DI, several smaller classes can be wired up, creating flexibility in both usage and unit test
- Increase modularity, low coupling and high cohesion
TDD (Test Driven Development)
This design does not guarantee your code free of bugs. In cons of costing time at design phase and layering effort, it has the benefit of:
- Easy to mock object for unit test
- Easier to refactor because of unit test and modularity
- Easier to maintain / extend
Some useful sources
Service Locator Anti Pattern
Using Decorator for cross-cutting concern
My 2 cents:
The benefit of using interface (also apply for composition ofer inheritance)
Doing top down design / DI design
Final Thoughts
Those designs and strategies are not the key which will determine your application structure. It still the architect who will determine it. I prefer to follow some principles such as SOLID, KISS and GRASP, rather than deciding what is the best structure. It is said that Dependency Injection follow the most of those principles, but too much abstraction and incorrect components design will resulting the same with the misuse of singleton pattern.
1.a. Make methods static and call (by all classes and concurrent threads) directly as MyMethods.someMethod();
1.b. Make methods non-static and at the time to call them, instantiate the whole class by MyMethods mm = MyMethods(); mm.someMethod();
The choice between these two depends on the functionality of MyMethods.class
. If MyMethods
is supposed to be stateless then it's a good approach to go with static
methods. Otherwise, if one method call depends on another and MyMethods
have states (i.e. non-final fields) then use the second option.
Use Strategy pattern stated at https://en.wikipedia.org/wiki/Strategy_pattern (code attached here with).
Use this pattern if MyMethods
are to be extended with different classes for different purposes and if you select which code to run depending on your context. As the wiki says, if you the algorithm to be used is not known before the runtime (depends on some conditions) this is the way to go. According to your specification of MyMethods
you do not have such problems.
Use dependency injection stated at https://en.wikipedia.org/wiki/Dependency_injection#Java
Same answer as above. The thing with dependency injection is in inversion of control. A class that uses MyMethods
does not know about actual the implementation of MyMethods
, but the injection of real implementation is delegated to some higher-level authority. It abstracts external dependencies from the context where it is going to be used. Again, if MyMethods
are to be stateless and constant (not planned to change, and the behavior of methods within the class does not depend on the context where they are used) you do not need these patterns as it would just mean over engineering.
I would conclude that you should use Strategy or DI pattern if logic of MyMethods
depends on the context from which they are run. If this is constant (e.g. Java's Math
class does not care who or in what context someone calls sqrt()
, max()
or pow()
) then static methods are the way to go.
Regarding problems:
Problems you stated are not present when you use MyMethods
with static
methods. You will have to test whether your methods return correct values for particular arguments and that's it. I don't believe that there would be much more trouble testing actual implementation of Strategy
in Strategy pattern or implementation of interface that are injected through Dependency injection. What might be harder is testing the classes that use strategy because sometimes it's not easy to recreate the context in which particular strategy will be executed as it often depends on user input, but it's definitely not impossible. Dependency injection is, as far as I'm concerned, great for testing because you can separate unit under test from dependency which you can easily mock.