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 asMyMethods.someMethod();
1.b. Make methods
non-static
and at the time to call them,instantiate
the whole class byMyMethods 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 methodsThats 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.
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)
Non-static
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:
Disadvantages:
1a / static class / singleton pattern
Advantages:
Disadvantages:
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:
Must be stateless
. It is easier to debug and unit test if the class is statelessThe disadvantage:
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:
Developers tend to put the logic in other
stateless
class, or at least methods such as: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:
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:
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.
The choice between these two depends on the functionality of
MyMethods.class
. IfMyMethods
is supposed to be stateless then it's a good approach to go withstatic
methods. Otherwise, if one method call depends on another andMyMethods
have states (i.e. non-final fields) then use the second option.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 ofMyMethods
you do not have such problems.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 ofMyMethods
, 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, ifMyMethods
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'sMath
class does not care who or in what context someone callssqrt()
,max()
orpow()
) then static methods are the way to go.Regarding problems:
Problems you stated are not present when you use
MyMethods
withstatic
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 ofStrategy
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.The main question: code reuse
Because you're not considering any cut-&-paste, I think you mean:
What you're asking is nothing special or specific. It's common that code is reused, either within a single app or across multiple apps. The common answer: use object-oriented design/programming. Put the code in a class, create an object as an instance, call the object...
1a. Reuse via static methods:
1b. Reuse via non-static methods, with object instantiation:
Use Strategy pattern
Strategy pattern can be good practice. But it has little to do with your overall question. Strategy pattern is used for a specific reason - to swap the implementation of algorithm/processing logic "on-the-fly" without impacting the caller.
Use dependency injection
Dependency injection is used for these reasons:
This can be very good practice, if used appropriately. In your case, this could only ever apply under option 1b. Dependency injection is all about object instantiation and provision into variables.
Problems:
Some people would say Unit test wont be possible
1a. See above
1b. Memory Load
A small issue. Depending on the data within each object instance (the instance variables), each object instance could be as small as a dozen bytes or as large as megabytes - but usually slanted towards the lower end (often < 1kB). The memory consumption of class code itself is not replicated each time the class is instantiated.
Of course, it's good practice to minimise the volume of objects, according to your requirements - don't create a new instance if you already have a useable one. Create fewer object instances and share them across your app - passing them into constructor methods and setter methods. Dependency injection is a good way to share object instances "automatically" without passing them into constructors/setters.
See above
See above