I have an existing java class which is having some functions implemented based on interface. Now I want to add new class which will have same functions and will get instantiated within constructor of my original class. So consider below example,
So if client class calls add() method, it should be Add() from class B. Limitation i have with my code is i can make changes in class A only. This is needed for backward compatibility where i want Class A to be untouched. This is because this class A gets instantiated through various scripts and method like Add() are used.
Thank you in advance. I hope my question is clear.
You say you have a class with functions based on an interface. I don't see an interface. The new class will be instantiated from a constructor of the original class. I don't see that happening either. If you mean replacing the current instance with another, that's not possible. You say you can only make changes to class A, but you also say that class A needs to be untouched.
Please explain clearly what the goal and the problem is. Why do you need to replace the behavior of A?
Is it possible? like can i cast an object to subclass object so that it will start referring methods of class B.
This looks like something (if I've understood you correctly anyway) closer to a factory method.
You provide the details to the factory method, and that returns the correct subclass.
Obviously, if your current code isn't set up for this then there will be refactoring required, however you're going to need to refactor anyway.
No, you can't do that in Java.
The problem is really that your design is inflexible and too coupled to a specific implementation class. It's too coupled because you have the code for creating a dependency mixed in with the code that uses a dependency. You need to refactor your client code (scripts) where this is happening so that you're passing in the implementation that was created elsewhere into the scripts that are programmed to the interface and use it. That is, if you create it, don't use it. Conversely, if you use it, have somebody else create it for you.
Along comes a change in requirements where instead of A, you have to use B now. Now you have to go through all your 1527 scripts and find where you're instantiating A with new A() and change it to new B() instead. What a pain, right? So, you're thinking "Wouldn't it be sweet if new A() actually created a B object instead?" -- Yeah, I can see how you might think that would be sweet but that's NOT how Java works.
You need to refactor and separate creation from utilization:
If you are able to refactor this to Dependency Injection pattern, that might be even better:
You're going to have to make more changes than you probably want to right now but this is the price you have to pay to make future changes easier to make.
In the future, if the same kind of situation arises with your B implementation class that needs to be changed to a C implementation class, you only need to go to the createA script to upgrade your program and all the scripts will follow without any change or ripple effect whatsoever.
Junilu Lacar wrote:That is, if you create it, don't use it. Conversely, if you use it, have somebody else create it for you.
This bears emphasis.
This problem explains exactly why, when a methods needs to do something, you pass the things it needs to do it with through the method parameters, or you assign them to fields from constructor parameters. This is what Junilu means by Dependency Injection. If the objects you require need to be created on the fly, and you don't know (or don't want to know) the precise type, you pass an abstract factory instead. A good example of this are database connections. When you have a repository of entities that need to be retrieved from a database, the repository needs to open connections on the fly, but you don't want to know the exact type of the connection, because you might want to swap to a different database later. Here's how that looks:
The getThing() method needed connections created on the fly for it to do its job, but instead of creating them itself, it asked the abstract connection factory to create the connection for it. That means it doesn't need to know about the exact subclass of Connection. It also needed an AbstractConnectionFactory for it to do its job, but instead of creating a factory itself, it asked for one in the constructor. Again, it doesn't need to know about the exact subclass of the AbstractConnectionFactory. When your application switches to a new database implementation, all that needs to change is that the application's entry point substitutes a new AbstractConnectionFactory implementation for the old one, and the rest of the application will remain working the same way it always did.
* Note that in this example, I used the names AbstractConnectionFactory and connectionFactory.createConnection(), because those names clearly reflect the concepts we talked about earlier. In an actual Java application, you would use DataSource and dataSource.getConnection().
Anyway, to deprecate A, just mark it with @Deprecated, let all new code you write use the interface that A and B implement, and after a while you can remove support for A completely.
vijay jamadade wrote:But I cant change this class to interface. Now what i am planning to do is create 2 service classes.
What do you think about this approach?
I agree with Stephan, you still don't address the root cause. What exactly is preventing you from turning A into an interface? Give us a concrete code example so we can understand what you are facing. Otherwise, it just looks like you're choosing to implement a kludge so you can avoid doing something that will improve quality but also bears the cost of carrying unmanaged technical debt.
vijay jamadade wrote:Thats why i said i cant change this class and i have to live with it.
Famous last words.
The thing with pain is that one way or another, sooner or later, you're going to have to deal with it. Skirting around a problem may make the pain temporarily go away but when it comes back later, and you can bet it will come back, its bite will be even more painful and more difficult to ignore or work around.
If your API provided an interface in the first place and clients, no matter how many there may be, were programmed to an implementation of that interface, then you shouldn't be obligated to take on the burden of maintaining support for those clients. Those clients should themselves be refactored so that they are resilient to change.
In practical terms, you may have to give clients reasonable time and means for transitioning to a better API design. I strongly encourage you to consider Stephan's suggestion and provide a factory instead. To me this is the best way forward for you. Your way may be expedient in the short term but in the long term, you'll be caught in a trap of your own making and I'm afraid you'll find it difficult to extricate yourself from it when you realize how big of a mistake you made. What's more, clients will hate you (or your company) for it because you led them down a path that would make migration/refactoring even more difficult and costly.