I am doing my best to write better software. Having been a long-time Java programmer and then learning ActionScript, I've come to appreciate some of the workings of ActionScript. I'm a self-taught programmer (about 13 years ago I learned not counting BASIC in high school). AS quickly taught me what async means and was a shock having worked in Java.
Now I've been into AS for a number of years and there are some lessons I have learned from it that I have applied to my latest server-side workings. I want to be able to identify and learn design patterns better. I want to know when I should use them and when I should not (or when better alternatives are available). If any are inclined to help me, I'd like to post what I did for this latest project and to get feedback on if I could do it better and what is right and wrong about my design. Here are the bullets.
1. The program has an entry point which starts a Thread pool and a ServerSocket.
2. Client connects to ServerSocket - ServerSocket gets a Thread and starts "MainService"
3. One MainService exists for each Client that connects. MainService is an IService:
4. A single handler is created by MainService. That handler is MainServiceHandler and it's an IServiceHandler
5. MainServiceHandler is sort of a "super handler" in that it has a reference to every kind of handler (in a Map) that is needed for my implementation. In each IServiceHandler's handleMessage() I implement any custom flow needed and then invoke the service.
6. When the service is done running, the MainService is notified. It in turn simply hands the message off to MainServiceHandler which causes some other handler to fire which causes some other IService to run and so forth until the workflow has been completed.
By designing in this way, I feel I've created a fully async flow. In each of my IService.startService() methods, I get a Thread from the Thread pool and then have it execute the IService runnable in question. Nothing moves forward until that Thread is done.
There are awkward parts to my design, I'm sure. I could have made another layer of abstraction and added IServiceHandlers to that object, for instance. But, in general, is my design sensible? I did it this way for a few reasons :
1. I can report where I am in process with this flow so I can feedback to the client as to percent done.
2. I have created a flow graph so to speak. If I have to modify anything, it will be contained to three objects - the MainServiceHandler, the IServiceHandler and IService in question. MainServiceHandler pretty well describes the entire implementation.
3. It should be obvious and simple to future developers how to extend the system with more services - even though it's probably a more verbose process than it has to be.
4. I can be sure that some long running processes complete before the system moves on.
5. I should never have deadlock issues as there's only one Thread in service (per client) per IService and they run iteratively.
Besides helping me understand if there's a better way to accomplish software construction, I have a question about frameworks as well.
For the first time, I integrated Guice into this process. I was excited to use it and I see some benefits to it. However, it seems Guice primarily gives you a centralized Object map and makes code really verbose and take longer to develop. Given that all of my objects, other than the IServiceHandler implementations don't take dependencies in their constructors, does it make any sense for a framework like Guice to be introduced to the process I describe above? What do I gain by this?
You gain the ability to make the code easier to test. With dependency injection, the code just knows about the interface IAMFMessageReaderService. Which means your test class is free to pass in a mock object rather than the one your code happens to be using.
I bolded the keywords that you can google for more information on those subjects. Sometimes part of learning is just finding out the right questions to ask or terms to look up!
The first design choice that jumps out at me that I would question is having IService extend Runnable. This is related to design principles that deal with the separation of concerns: Interface Segregation Principle and Single Responsibility Principle. Requiring implementations of the IService interface to also be Runnable seems like you're inappropriately coupling/mixing two different concerns. After all, a class can implement more than one interface in Java so why force a Service implementation to be Runnable as well?
Also, I'm not a big fan of the "I" prefix for interfaces convention, but that's a personal preference. I prefer to use packages and concrete prefixes, e.g. JdbcFooDao, Hib3FooDao, XmlFooDao (possible implementation classes of the FooDao interface). In the same way that the Java Collections has ArrayList and HashMap as implementations of the List and Map interfaces, respectively.
Another smell is that all the method names have "Service" in them -- kind of redundant since the interface itself is already called "IService" (again, I'd prefer just "Service" for the interface). Code that results from such a definition tends to be repetitious and wordy:
Another smell: stopService() is declared to throw Exception -- this seems too general for this particular method and suggests that you don't know what the exact behavior is you want. Maybe leave Exception out of the throws clause until you have more specific checked exceptions that you can specify for this particular method.