I have "library" of classes where the majority of classes need to use a specific "Website" object. I'm wondering which is the best approach to allow these classes to access this "Website" object?
1) Use a registy
2) Use a singleton for the "Website"
2) Store a reference to the "Website" object in each class that needs access to it, which means passing the object through the class hiercharchy. This adds unneccesary responsibilites to some classes.
3) Pass the object to the methods that require it, which also means passing the object through the hiercharchy, but no atleast objects don't have to be responsible for remember the "Website" using this method.
4) Pass object of composite class to the "child" classes, such as ->getParent()->getParent()->getObject()
5) Use a factory? I'm not sure about implications of this
Or perhaps something else?
Your help would be greatly appreciated as I've hit a brick wall here.
The "Website" object isn't global. Basically it's an object that models the website that the "library" is intended to work with. It's simply a domain model of the Website and contains data that is required by the classes in the "library". I keep calling this a "library", but in reality it's a sub-system of a larger project - it's not a full-blown library, not sure if that's relevent or not.
A friend is going to post a link to a (albeit incomplete) sequence diagram to give you an idea of how the classes in this sub-system interact with the Website object. I would post it myself, but I'm not allowed to post links untill I've made 5 posts
TomB, good point, and thanks for your input. I was going to implement that encapsulation when I've figured out this little issue. Im focusing on the Forum objects at the moment, I think it's becomming clear that I could be focusing on them too much.
Anyway, back to the point, surely the same basic question remains, what is the best method for accessing the Website object from the Page and Page Strategy objects? Lets assume that the Website does instantiate the Forum object, this change probably does remove some of my original options.
Should I pass the Website to the forum object upon construction? Doing so will then place additional responsibility on the forum, the responsibility of keeping track of an object that it doesn't use. it would simply remember the Website so that it could pass it on to the template objects, which again will need need the same unnecessary additional responsibility. Surely there's a better approach. Perhaps passing the website through the method calls. I think this is the better approach of the two, but doing so "bloats" the method signatures.
I can't make a decision as to which approach is best. I guess for me, I prefer passing the object through the paramters, it's messy, but probably not as messy as adding additional responsibilites to classes. I guess I was hoping for some miracle solution - or maybe something I've missed - I think it's becomming clear that these are my only 2 options?
Hi Shrike, thanks for the input. The problem I am facing is that the Page and Page Strategy objects are created at various points in the "library", so passing the Website object around to all the places that needs it becomes messy. For example, i have a "special case" for the "Login Page" class as it is required by a class that shouldn't know about the "Website" object.
To elaborate on my previous statement, every request for content is managed by a "Scraper" class, however, some forums require you to login to use search functionality, so the "Scraper" has an "Auth helper" that determines this for me, if login is required, the "Auth helper" has to use the "Login Page" object to perform the login. Now this is where it gets tricky, the "Login Page" needs access to the "Website" object, but the "Scraper" class doesn't, and shouldn't, know about the "Website" object. Likewise, the "Auth helper" doesn't.
So if I follow the pattern of passing things through the hiercharchy then I must pass the "Website" object to the "Scraper", which in turn has to pass it to the "Auth Helper" which again has to pass it to the "Login Page". The "Website" object is getting tightly coupled to objects that shouldn't know about it.
I could go another path, and create the "Login Page" at an earlier point where the "Website" object is available, such as when I create the "Scraper" object. I think this may be the approach I took - it seems the most logical, I'm not sure, I dont have the code to hand. I will check the code tomorrow.
I guess I should have mentioned this part of the problem originally, as it is a major piece of the puzzle.
Karl, you do not publish whole class hierarchy so it is hard to comment .. but your diagram raises some issues:
- there is a chain of 4 called methods with same name: Client->getPosts, Forum->getPosts, GetPostTemplate->getPosts, SearchPage->getPosts. It looks very weird for me and indicates problems with responsibility of classes.
- What is the purpose of "Members Page" class? To deal with page or ID? Why do you target "page" class with method getMemberId?
Let me explain the classes a little better, from the bottom up:
The Page Strategies provide different implementations of the Page class, for example, a single page strategy could determine how to extract a post from a single vbulletin thread page, such as this one. However, this one strategy probably wont work for all vbulletin forums, so this has to be flexible. For example, the same code that works on one vbulletin thread page may not work on another vbulletin thread page.
The Page class itself is mainly responsible for determining the correct strategy for each page. At first it's a try every strategy approach, but once it finds a strategy it knows works it will select this strategy in future. This page may also call the "Scraper" for the page content, and pass that to the strategy - to speed up the strategies. However, sometimes strategies have to get their own content too. I can probably get rid of Pages, and add this code to seperate methods of a single "Page" class - it wouldn't be very cohesive, but it would simplify the design. I was going for full flexibility, but in hindsight the Page classes are probably redundant.
The Template classes define algorithms with "plug-in" points for creating flexible solutions to the various problems, such as get posts for member. For example, one version of the get posts "algorithm", to get posts for a specific member from a vbulletin forum, could be:
1) Get member id from Member Page
2) Get all posts from Search Page
3) Get each post's content from Thread Page (i.e. this page)
Repeat #3 for each post
For this to work you have to provide the 3 pages to the algorithm, at the inception of this design I thought this interchangability may be required, but as I previously mentioned, the Page class is probably redundant.
Finally, the Forum class provides a single interface to the set of classes that it works with. When you subclass the Forum Abstract class, for example to support Vbulletin forums, you specify which version of the get posts algorithm the forum will use, for example, Vbulletin uses the 3 steap approach I outlined earlier in the post, but Invision may not work for that approach.
Going back to my original problem, I think i'm going to go with the "pass the Website object through method calls" approach. It seems the most logical, but I first need to see how it fits in with the problem I mentioned last night. It makes sense that the Website object should be passed to the methods. When I thikn about the purpose of the Website object it's basically acting as a Parameter Object, most Pages and Page Strategies only need the domain, but sometimes the locale is also needed. If I were to remove the object and pass only the data I needed, then this approach would be the most practical.
As a final note, in case the above text didn't clear it up, the purpose of the Members Page class is to model the Members page of a vbulletin forum. The call to getMemberId() simply scrapes the member page of a given forum for the id of a member specified by their username.
Anyway, thanks for input, it has forced me to reconsider my need for each class in the design, and doing so has allowed me to spot the possible redundancy in the Page class. I'm not sure about removing the Page class just yet, but It's definately something I need to consider.