The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Design for Scalability

Hi,

I am very new to design patterns and mostly OO methodologies. I was writing a simple client code that requires to obtain an interfacing Internet address of a machine that the code is running on -- whether being connected directing to the Internet or through LAN.

At first, I could just simply write a code to perform such task right into the client code but it's not expandable. So what does a good designer do? He writes an interface and its implementation, great. So, whether my implementation acquires IP address from a LAN router, parsing a downloaded HTML from a site that runs a script asking for external IP address and retrieving it that way, or some other method, in either case, I can plug in the implemented interface in my client code and accomplish the same task, i.e.:

interface IPInfs { String getIP(); }
class RouterIPImpt implements IPInfs { //... }
class HTMLParseIPImpt implements IPInfs { //... }

// Client class that uses any of the implementations
IPInfs ip = new RouterIPImpt(); // OR
IPInfs ip = new HTMLParseIPImpt();
// etc.

So if another guy comes years from now and w
My questions is, what if the client code needs to stay intact? That is to say, the code that utilizes IPInfs for its work cannot be changed, yet it must provide some flexibility that any implementation that is created in the future can be easily be plugged in and used instead of the current one?

One way (at least in java) is to have a persistent property file with pairs of value that the client code can extract a single implementation value (class name) and use it to load an appropriate class, i.e.

// ip.properties file
classtoextractip=my.package.RouterIPImpt // OR
classtoextractip=my.package.HTMLParseIPImpt

// Client code
// Loads a string value for "classtoextractip" into String ipStr
IPInfs ip = (IPInfs) Class.forName(ipStr).newInstance();

Basically dynamically load the implementation class at runtime and reference it to a IPInfs-type variable. So from now on, any new implementation that has to be used in the "client" code can simply implement the interface and change the "classtoextractip" value to point at such concrete class.


Is this an optimal way *if* the client must remain unchanged? Is it flexible and scalable? Are there other ways to accomplish the same task with the given requirements?


Sincerely
Zpitta Send private email
Wednesday, December 17, 2008
 
 
Google for "dependency injection". Martin Fowler does a good job of describing the options available:

http://martinfowler.com/articles/injection.html

By the way, I hope what you've posted is just an "example" that you are trying to wrap your head around. There are times when implementing this type of structure is warranted and times when it is not. In this specific case it probably isn't. Just create a single class that gets the IP address and be done with it.

Wednesday, December 17, 2008
 
 
I agree about the use of dependency injection. Instead of worrying how your code will get its IP address (or whatever), just have it, or some component that can retrieve it, passed in when your code is initialised, or passed in to each function that uses it. That way the caller can decide how to do it. This is sort of what you're hinting at by suggesting using a file containing the name of the class to instantiate, but it's much more flexible not to force anybody to use files at all (what if there isn't a filing system?) and let the caller decide.

Then, if they're not using files, or they've only got one IP address implementation, or whatever, they aren't forced to jump through any hoops to make it work, they can just pass in the thing they want and it's done. Also works well when "they" are you -- when you're first putting the whole lot together, you can concentrate on the meat and save the supporting infrastructure (loading files, fiddling with reflection, etc.) for later, and it seperates out these two types of code as much as possible.

Whether you call it 'dependency injection' or 'good form' (I prefer the latter) this is certainly one of the most useful programming tactics available.
brone
Wednesday, December 17, 2008
 
 
Brone,

Thank you for your response. I am quite bit confused about your explanation. Obviously, the example I provided is not for serious application but rather a simple tool to update the DNS server/provider when the server's IP is assigned dynamically and altered occasionally. The whole quest is to learn more about design methodologies, their pros and cons, and ultimately being able to decide to choose an appropriate one for a certain requirement and criteria.

Anyway, you mentioned that I should delegate this task (IP acquisition) to some other component and use some sort of setter or constructor to instantiate this retrieving-IP object. This so-called component is basically has to be designed the same way I described to be able to dynamically select the implementation so I don't see how it leaves the usage of configuration out of the scheme! i.e.,

// Client code
public IPInfs ip;
public ClientCLass(IPInfs ip) { this.ip = ip; }
// At client entry point module
ClientClass cc = new ClientClass(// some IPInfs implementation);

Ok, in this case, according to the second paragraph, I can tweak the code to what you suggested by passing the implementation class by means of, let's say, main() parameter which eventually I have to use Class functionality to get an appropriate instance. So basically we went from determining the implementation from a property key-value to command line argument. I don't see a whole lot of gain here as far as the overall design. And from the IoC pattern, I see the containers basically utilizing configuration files (i.e. XML), reflection, and class loading to navigate or realize the actual plugin
Zpitta Send private email
Wednesday, December 17, 2008
 
 
Sorry for my poor explanation. I've been using this technique for ages and ages, and I've internalized it thoroughly, so my thoughts on the matter are pretty subconscious and non-verbal now. (I only found out it had an Official Name in the last couple of years!)

My problems aside, and with the worry that I'll just dig myself deeper into incomprehensibility by continuing, the issue is (as you've noticed) not with the basic approach you suggest, which is exactly right.

The point is simply that given an interface there are multiple possible implementations that conform to that interface, and that it is virtually always the case that the caller is better placed to decide which implementation to use than the callee. And you can make it easy for the caller to decide by having each component *told* about its interface with the rest of the world, rather than working out which to use itself.

The called code is then simpler, as it has everything it needs, and no more or less, on a plate. It is clearer, because instead of having to talk to each dependency on the dependency's terms, it talks to everything on its own terms, as dictated by the interface it requires. The calling code then has the control over it that it needs, since the called code interacts with anything else entirely through this intermediary, provided by and under the control of the caller. And the calling code can end up simpler, too, if circumstances permit, because if it doesn't actually need to do anything complicated to work out which implementation to use, it can just create a suitable object directly and pass it in.

And, best yet, with luck, over time the interdependencies between different bits of code stay more comprehensible -- for at any given point in the code, only using only things that have been passed in to that component are being used, and there's no use of random bits of global state.

This is why it's called dependency injection: the dependencies are still there, but they are provided to (injected into, if you will) the called code, rather than having it find them for itself.

(XML, command line, etc., is a bit of a side issue. If you don't need to pick an implementation at runtime, then you don't need to do this, and yet your code can still use the technique and reap most of the benefits. But if you need to pick an implementation at runtime, you have to pick one somehow, and reading its name from an XML file/command line/etc., then using reflection/dynamic loading/etc. to find the code for it, is one good way of doing that.)
brone
Thursday, December 18, 2008
 
 
Brone,

From what I can see, dependency injection is basically an orchestration of object creation by decoupling the callee from caller. The process boils down to two options: obtaining the dependency or having it "given" to the caller, i.e. set the callee reference in a setter rather than have the reference be assigned by the direct implementation in the constructor.

Now, the actual "giving" or "injection" of callee can be "directed" by some external entity (i.e. property file or XML values coordinate what object should be created by the caller). In other word, the caller has no longer control over what implementation of asking type is given to it and the responsibility of collaborating of what callee reference lies somewhere else -- sort of "injecting" this reference in to the caller. In simple terms, the configuration files merely provide "wiring" or "association" between decoupled objects. So you are absolutely right about the last part being trivia to the whole picture. I lost the gist of the pattern by focusing on "how" the injection is performed rather than the overall design and its benefits.

Frankly, one of the perks I can deduce from the whole scheme is that the testing has just gotten easier and objects can be examined in isolation more independently from each other. For someone who is unfamiliar with patterns and struggle with their structure and use, I view decoupling as being a quintessential necessity of every design. You know, when they first talk about all the OO paradigms, they never point out why OO has been thought out the way it has -- to a virgin mind, the whole notion is murky and warrant no benefit. I believe it's imperative for teachers to come out and actually blurt out why we have the tools that OO has to offer.
Zpitta Send private email
Friday, December 19, 2008
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz