Mansour, it is interesting. Maybe it would be easier if we simplify the existing classes (and application code that uses them) to use only a subset of the methods available (some of them are already deprecated); the refactoring based on design patterns will be easier at that point.
Jacopo On Mar 26, 2012, at 11:25 PM, Mansour Al Akeel wrote: > Hello all, > I am looking at the code for the GenericDelegator. We have 2913 line > of code in one class !!! > This class handles a lot of functions and I think it's the most > critical to the application. These functions can > be distributed among more classes, providing better maintainability > and extensibility. I can see the best pattern to > be used here are chain of responsibilities and decorator, where each > class provide a defined set of functionality and implement the > Delegator interface. > > For example, currently this class handles caching, configurations, > model Reading, encryption, logging, and the main function of accessing > data. > It's true that the interface it implements is huge (1217 line of code) > but mostly comments and documentation. CoR design pattern will allow > easier > unit testing, and better maintainability and allow to extend. So let's say: > > GenericDelegator implements Delegator (Interfaces with the external world ) > ===> passes calls to SQLQueriesDelegator (build the sql through > implementing findXXX()) > =====> passes calls to LoggingDelegator > =====> passes calls to ModuleReaderDelegator > ======> MultiTenantDelegator > ========> CachingDelegator > ==========> EncryptionDelegator > ============> etc > > Each delegator does some work and calls the next one in chain until > the final functionality is achieved. Of course customization will be > easier. > For example, if I want to swap the current caching implementation with > something like memcahe or ecache, I have to change in many places in > the single 2913 line-of-code class !! With CoR all I do is swap the > current CachingDelegator class with my own. Let's say I want to access > the file system > or jcr nodes in the same unified way I access the db. In this case all > I need to do is add some thing like "RequestRoutingDelegator", and > JCRDelegator. > The RequestRouting decides how to access the entity in hand and based > on that uses either the SQLDelegator or JCRDelegator. > Those who did xml processing with SAX2 filters (CoR) or worked with > java IO streams (Decorator Pattern) will appreciate the concept of > separating functionality. An example in JavaIO, if one needs to ecrypt > a stream, unzip the input, or zip the output .... etc. All these is > done though decorator pattern. > > The whole chain can be created and constructed in the factory method > (or using a container config in the future), so existing applications > should not see any changes, as all of these delegators will be hidden > by package visibility (except for the first one in the chain). > In the factory method (hopefully in the future called through a > service locator), we can do something like: > > Delegator encryptionDelegator = new EncryptionDelegator(); > Delegator cachingDel = new CachingDelegator( encryptiongDelegaort); > ..... > ..... // may be do some configuration as we dont want every delegator > to configure itself by reading a file or looking up a > // of a property, in case we used a container in the future. > .... > Delegator realOne = new GenericDelegator(); > > static methods can be moved where needed and ensure there's not > duplication of functionality. At some point we may consider > making them instance methods. > > To avoid DRY, we can create a class (implement Delegator) with default > functionality of calling the child (next) delegator. > Then just extend it and override whatever we need. This way we keep > the code minimal, and clean. > > At the end, assuming no advantage gained, who wants to maintain 2913 > line of code with some lines more than 100 character wide ??!! > > I am not sure about the amount of work it takes to finalize it, but I > can see some difficulties. For example, assuming two different methods > belongs to two different layers, need one of the static methods in the > GenericDelegator. This will lead to duplicate code, we are going to > call static methods in other classes, and creating a mess again. > Another issue is when an object needs to be accessed in different > layers. This means we need to keep a reference for that object in > multiple places. What if it changed in one of the layers ? For > example, ModelReader is accessed in cloneDelegator and in > decryptField. Both of these methods belongs to different Delegators. > cloneDelegator goes into GenericDelegator, and decryptField goes into > EncryptionDelegator. What if the model reference changed in one of > them ?? Caution needed ! > > Another problem is when the order of calls in GeneralDelegator is not > consistent. For example, in one method we accessed the cache then we > did the logging. In another method we did the opposite, we logged, > then we accessed the cache. When done in layers, it will be harder to > do this. > > Another alternative is something close to the composite pattern, where > we have Helper classes with a subset of Delegator implementation. The > GenericDelegator class, instantiates them and keeps a reference to > each of them. Then calls the appropriate method. This really simple, > but less > sexy, as extending the functionality still requires looking between > the lines. It's just breaking the HUGE class into smaller classes. > > Not sure if I missed something. Other patterns could fit better. If > someone captured something, please let us know. Ofbiz team can decide > what path to follow. > > Since this component is critical to the application, some may prefer a > more senior resource on the team and more skilled > with the internals of ofbiz, to handle this task. However, I don't > have a problem giving it a try. This task may sound small, but I don't > know what is a head of us. Someone with deeper knowledge about the > entity engine can comment. > Alternatively, one of the commiters, can adopt this task, and do the > cleaning as needed. For example, every while and then revisit this > huge class, and > break separate the functionality into the corresponding classes. > > I didn't want to open this subject now, as I am aware of the cleaning > being performed by the team. However, if we can not do it now, and we > agreed about the path, it can be added to JIRA for future reference. > > Please discuss and share knowledge.