Showing posts with label object orientation. Show all posts
Showing posts with label object orientation. Show all posts

Wednesday, January 13, 2010

My Brother's Term Paper - Strengths and Weaknesses of Object-Oriented and Procedural Programming



My 17 year old little brother, Gian Dionisio, is currently working on getting into college to pursue a computer science degree and has always expressed a genuine interest in software engineering. I'm on his back 24/7 when it comes to good grades, school etc. so it was a pleasant holiday present when he showed me his senior term paper in which he received a 98 out of 100. Glad he hasn't caught "senioritus" like his older brother did! I'm posting this more for selfish reasons as it's something I'd like to point people to for a totally objective view when it comes to object oriented vs. procedural programming. I've probably given Gian about a 1 minute dump of my own thoughts on the subject but as you'll read, his essay is based entirely on his own research. so here it is, good work Gian, very good work.

Strengths and Weaknesses of Object-Oriented and Procedural Programming by Gian Dionisio

The process of designing a program is broken down into four steps: analysis, design, implementation, and maintenance. Analysis refers to determining if a problem is solvable, design refers to the process of determining how to solve the problem, implementation is the actual coding of the program, and maintenance refers to the debugging and modification of the program. As the process progresses from one phase to the next, costs rise higher and higher, with maintenance being the most expensive (Lambert). Currently, many programmers use one of two paradigms of programming when designing and implementing a program: procedural and object-oriented. However, many programmers have begun to stray away from procedural programming in favor of object-oriented
because of the way object-oriented programs handle data, which allows for more dynamic and flexible programs. Even though object-oriented programming offers more flexibility than procedural programming, programmers should not perceive procedural programming as obsolete because both paradigms have strengths and weaknesses that do not make one necessarily better than the other.

Procedural programming is best described as a one-way system, using only variables and functions, with no major data grouping, which is the primary strength of object-oriented programming (gabehabe). This paradigm emphasizes on conciseness and reaching the end result as quickly as possible, solving only the problem presented and nothing more, making procedural programs very straightforward (Weisfield). John Barton of IBM notes that generations of programmers have essentially been taught to create a list of instructions for the
machine on how to read data (gather ingredients), how to use equations and functions with subroutines (cook the meal), and how to display the product (a finished meal) (Waldrop).

While the design and implementation of a procedural program may be straightforward, especially in smaller scale projects, as the programs grows larger in scale, both design and implementation become increasingly more difficult as different subroutines are being performed on the same set of data at the same time (Waldrop). When the program is finally functional, modification is made either difficult or impossible because changes in one part of the program may affect the rest of the program as a whole (Archwing). Because procedural programs are not able to be readily modified, maintenance costs become very high. For example, think of a 10-story apartment complex with 10 apartments on each floor, and a development lot for 100 houses; the lots of houses may cost more to construct than the apartment complex, but the houses are, for the most part, self-contained with their own
utilities and walls, versus an apartment, which shares utilities and walls, therefore adding another house would be far less expensive than adding another apartment to an apartment complex (Orr). This problem of modification is solved with the principles emphasized in object-oriented programming.

The main strength of object-oriented programming is its ability to handle data by taking advantage of the way we perceive of environment with the use of objects (Parr). Objects can be seen as separate entities, each with their own characteristics. These entities all share two characteristics: states and behaviors (Sun). For example, a
dog has 'states' (name, color, breed) and 'behaviors' (barking, fetching) (Sun). Objects "encapsulate" these qualities, meaning that they contain data, otherwise known as states, and methods, otherwise known as behaviors (Orr). These objects inherit these qualities from blocks of code known as "classes"; a class can be seen as a blueprint or template of an object, much like how biologists group organisms into classes such as "bird" or ”mammal” (Waldrop). This use of objects gives a solution of designing large scale programs by writing small-scale software units that communicate with each other, rather than a huge system (Parr). This tactic of "divide and conquer” is employed in both procedural, with the use of subroutines, and object-oriented programs, with the use of objects (Nemirovsky). However, in a procedural program, the programmer must tell the computer how to perform an action and when to execute it, in contrast to an object-oriented program, where the programmer only tells the object what to do, and then the object performs it (Sherer). This means that a programmer doesn't need to know how or why an object works, but merely what it does; demonstrating the principle of information hiding, a method of encapsulation in which only certain details are able to be seen (Orr). Information hiding allows for large scale projects to run much more smoothly because programmers don’t need to know the implementation of a class to use it, and allows changes to be made without drastically affecting the entire program, as well as a certain degree of consistency within the application, as programmers working on the project are using the same base classes
(Lindsay).

When code is segmented into smaller sections, each with their own separate individual functions, programs become easier to work with. For example, in an object-oriented program, if a program needs to know the month in a two-digit format, it sends a message to a Date object with the message asking for the date in that format, rather than accessing the date from a variable floating somewhere within the program (Waldrop). Within that same program, another object can ask for the date in a three-letter format by sending a message that corresponds to that demand to the same Date object, and the Date object would return that date. Object-orientation also allows the developer to assemble his or her code in the same manner that he or she thinks about the problem (Waldrop). The developer can create an object named  ”Airfoil” and define a function that instructs the object how to handle data, and then the developer can send a message to the Airfoil object to calculate something, such as wind resistance (Chastain). By developing and debugging small components independent from the program, developers are able to isolate and test code more efficiently, and can assume that the program works as advertised, rather than guess where problem may lie (as with the case of a procedural program) (Chastain). By breaking up code into smaller pieces, object-oriented programs become easier to modify, and because of this many companies are interested in reaping the benefits of the object-oriented approach, that is, reusable and easily modifiable programs (Patrizio). Object-oriented programming can be considered better at representing the real world than procedural programming because object-orientation allows for more intricate and dynamic interactions as well as allowing non-technical workers to better understand and participate in the maintenance of a program because objects better appeal to the natural human cognition patterns (Archwing). An example of an object-oriented program’s ability to be modified can be demonstrated in a payroll program; if the program was written procedurally, the area that assesses the employee's paycheck would be in an "if-then" form, versus an object-oriented program, which would send messages to all the employee objects, which would then calculate all the paychecks (Sherer). Any modifications done to the procedural program might cause a chain effect in the program, making unintentional changes. Object-oriented programs also become easy to build upon in case the scope of the project grows larger
because of the principles stressed in object-oriented programming, such as encapsulation and inheritance, allow the application to expand quickly, and pre-existing classes do not have to be modified in any way because objects interact with each other through their methods and messages (Chastain).

Object-orientation offers many tools and principles that allow programmers to create dynamic programs; however, learning to use these tools can prove difficult. An entire way of thinking was built off of the procedural method, and “reprogramming” the programmers may take a lot of effort on the developer’s part before they can fully reap the
benefits of clarity and reusability (Waldrop). Procedural programming is action-oriented and solves problems through a series of logical steps, versus object-oriented programming, which looks at the entire problem as a whole and then derives a solution with the use of a series of reusable classes (Patrizio). The object-oriented approach presents a radically different method of method approaching software: code and data become merged into one single, cohesive unit— the object, and is an abstraction of a set of real-world things, such as "date" or "employee" (Archwing). Systems are made up of multiple objects, such as date, or processing, and make requests with messages which ask for specific pieces of information (Archwing). This different way of thinking can be met with resistance by some, as in the case or Chevron’s programming staff, who have difficulty grasping the concepts (Moser).

A method for tackling the problem of learning how to use object-oriented programming and concepts is through the use of graphical user interfaces (Moser). Graphical user environments have been in use by high school computer classes in hopes to intrigue students to Iearn object-oriented programming by showing the fun, enjoyable side of programming without having to touch the dense terminology of the industry (Demski). For example, many high schools have begun to use the BlueJ graphical user interface, which displays coded boxes and arrows that allows students to watch the concepts in action, and allows them to get a clear understanding of the concept (Dernski).

However, learning how to use object-oriented programming is not the only problem, but when to use it. Object-oriented programming, while very good at managing data complexity, is difficult to apply, which may be a major reason for programmers not to adopt object-oriented programming. Marc Funaro, a computer programmer, made a blog post detailing his experience in trying to incorporate object-oriented programming principles into his own programs, and stated that by doing so, had almost ruined his business (Funaro). Marc was under the impression that by incorporating aspects of object-oriented programming, his programs would become much more efficient and reusable (Funaro). However, this is only partly true, because in order to fully take advantage of object-orientation, one needs to learn how use object-oriented analysis and design; using an object-oriented language will not yield the promised benefits unless one learns to think in the appropriate manner (Buckier). Brian Carr, a seasoned software architect, stated that programmers who use an object-oriented language but are not thinking in an object-oriented way are essentially procedural programmers; additionally, learning how to use object-oriented programming does not make one a better programmer, but rather, adds another tool to the programmer's disposal
(CF OOP Debate). Carr also notes that object-oriented programming is better than procedural in one aspect: managing complexity. Funaro also complained about design and implementation time when using object-oriented programming, and that a project done procedurally would be completed much more quickly (Funaro). Mike Chandler, another object-oriented programmer, commented on this, saying, “There are some projects that procedural programming would be better suited than OOP, especially those that are smaller scale. When you’re designing a program, you really need to think about the customer’s needs. Will he wants to modify or extend the program? That’s when you need OOP” (CFOOP Debate).

In this age of technology, advancements are being made very quickly, however just because progress is being made, doesn’t mean we should abandon older methods. Object-oriented programming, though powerful and very flexible, is not without its drawbacks, suffering from longer design and implementation times. Procedural programs, though inflexible, are quick to design and implement, and may even prove superior to object-oriented programming is smaller scale projects if fast delivery is key. However for the mid to enterprise level software systems, the object oriented approach does cost a little bit more up front but extraordinary gains are realized during the maintenance and enhancement phase, which is where most cost is incurred.

Tuesday, December 15, 2009

Design Before You Develop - Intro to Class Diagrams with Quick Reference

If you already are a design-before-you-develop junkie like I am, daps to you. You can probably stop reading here and move onto the next blog in your queue. For the rest that are curious about it but don't know where to start, read on.  It's my opinion that you should get into the good habit of designing before you develop. Even if the design takes you 10 minutes on a scratch pad, a quick design session on a whiteboard or several days in a full blown modeling tool, you should do it.

Why? Because it's during those sessions that you and your team can express the intended inner workings of your software modules, poke holes in it, rinse then repeat as needed. You can focus on a list business of requirements, design the class diagrams and literally step through the requirements allowing you to articulate the path of execution that'll take place to fulfill each requirement. All of this is done without a lick of code. You discover design flaws early on, not during development. That's risky. You can even take it a step further and generate sequence diagrams against your UML model and requirements, they are invaluable! But sequence diagrams are outside the scope of this article, we'll get to it some other time.

I'll say it again and again that during the design process you need to forget the code. Coding should be the easy part. It's the design that lends your software scalability and flexibility. Without a proper design, you run the risk of causing not only future headaches for yourself and your team but more importantly, the business. Yea yea, without the design phase it's so much faster to release a product. Sure, you can tell your boss/client what you're doing is rapid development. Sure, your bosses will be happy for the time being cause you got it out so quick. You're the hero. You're the man. But who cares if you are spending the next year after your go live fixing code, trying to re-design after the fact and burning through company cash hand over fist. Or worse, bandaiding already production level code with cut rate code because everything is an "emergency" and you don't have the luxury of thinking things through anymore. All the while the boss that was singing your praises is no longer impressed that you put out a piece of software that constantly breaks. It's a vicious cycle and could've been avoided by dedicating a little more time to designing up front. Use the above as a case to your boss when you need a little more leeway to get some design cycles in in the beginning. Just like Ben Franklin put it:
An ounce of prevention is worth a pound of cure.
He was a pretty smart dude, and his quote certainly applies to software engineering. Let's put in that ounce of prevention by designing first eh?

UML to the beginner can seem pretty damn intimidating. In my comp sci class I remember cracking open the UML section and my eyes glazed over at all the fricking symbols. I thought to myself, how in the hell am I going to memorize all this? After I got some real world experience, it's really not that bad. Given, you can get pretty deep into it I find that I'm able to express a software model with only a few symbols. I thought I'd share with you some of the ones that I use all the time with Microsoft Visio however the symbols below are not application specific, they are universal symbols. Here's the quick reference:



These symbols should be more than enough to get you going. Below is a what a real class diagram might look like using all above symbols:



In the above class diagram, there are two applications being modeled at the same time that use shared components. A school app and a war game app. I've put together some questions I'd ask myself while making some observations of this class diagram:
  • Person is being extended by Student and Soldier. 
  • Soldier is in the common package. "Is this correct? Shouldn't it be in the wargame package? What happens if its not?" 
  • Soldier can have many pistols. "But is that true? Should it be a one to one? Should there be a collection manager of sorts to keep the api simple? Better ask the business."
  • Student can have many books via its multiplicity. "Same multiplicity questions apply here?"
  • Student is teachable, since it implements ITeachable. "Are the methods defined here really granular? Do they really belong in this interface? Is there anything else I can add to this interface?"
  • A pistol implements IWeapon so it must fire.
  • fire() on the IWeapon interface returns numeric. "Is this the proper return type?", "Should it be another return type?", "Are we going to calculate fire() based on the numeric damage its dealt?,  If so keep it. If not, let's redesign".
  • Soldier doesn't have an interface. "Does Soldier need an interface?", "Is there something that all soldiers must be able to do? Let me dig into the requirements and do some textual analysis."
  • Person has a firstName, lastName and age member. "Are these shared across all Person objects?". "Do they need to be specific to a subclass?"
  • All classes are have their packages before the name so it's clear where each class should exist.
This list could go on and on!

All of these questions should be addressed during design to ensure you and your team have a clear path of development. There are a bunch of open source tools out there especially in C# built into Visual Studio (and I'm sure Java has theirs too) that will parse through class files to generate UML diagrams ad hoc. For ColdFusion developers, at the time of this entry, Mark Mandel has been recently tweeting about a prototype he's working on that will generate UML using ColdDoc and UML2 tools. Initial screen caps looks promising, so it's definitely something look out for. For more info check out Mark's twitter.

I'll also add that it's important to note that these tools should not be misused. Meaning you shouldn't develop first just to produce a class diagram against off-the-cuff code. These tools should be used when you need a way to dynamically create accurate class diagrams from current code for a number of reasons:

  1. You may be working within an enterprise level organization where there are multiple teams with their own special access to certain aspects of the code. Developers often need to know how to leverage an API in which they have no access to the source. In my experience that's where these automated UML tools come in handy. You can print it out and ship it off to whatever team needs it. From the UML, they can discern the contracts in which they need to develop against.
  2. You don't have to deal with the headache of documentation maintenance.
  3. You need to reverse engineer and analyze design at a high level, especially if the code base is pretty huge and there's a lot going on.

So that's class diagrams in a nutshell and hopefully you'll give the design before you develop approach a try if you haven't already. It may seem like a long and drawn out process at first but believe me practice makes perfect. Take it little by little. Maybe try it with that new module that needs developing or that module that is in need of some major refactoring that you've been putting off for months. After awhile, you'll be zipping through design sessions quickly because you've trained yourself to identify patterns and problems during that analysis phase. It'll eventually become second nature and you'll soon feel dirty not doing some form of design up front.

Hope this helps.

-Micky

Resources
Beginning C# Objects: From Concepts to CodeHIGHLY recommend this book for the beginner UML/OO developer. C# is in the title but the content applies to all languages.

Friday, December 11, 2009

Quick Tip - See nested conditionals? Consider Guard Clauses

Just a quick tip when you are refactoring code with crazy nested if/else logic. As a rule of thumb if you see a method that has conditional statements check to see if these exists:

  1. Is there a private variable, let's call it x that is private to the function that is being set within each (or at least a majority) of the if/case legs whose value is a method call?
  2. Is x is being returned at the end of the method?
  3. You got nested conditionals?
Here is what it would would like:

Notice the:
  • result  private variable at the beginning of the function
  • result is being returned at the end of the function
  • Nasty nasty nasty nested conditional that sets result to the value of a method
If the above is true or the code you are refactoring looks similar to this, you might want to look into using guard clauses. Sounds more complicated than it really is. Basically see if you can refactor it like so:
  1. Remove the private variable thats being set during each if/else leg.
  2. Return the method or the condition that is setting it.
Simple as that. Here is the code above refactored:


Notice now:
  • No more nested if's
  • After each if/else leg, we are returning the method.
Given that some legacy methods you are trying to refactor are not as trivial as this and each have their own unique level of suck - this is always a good start. This keeps your method simple, less complex and highly testable because each execution path is linear. Guard clauses baby, a good alternative to the strategy pattern.

HTH

-Mick

Thursday, December 10, 2009

Understanding Strategy Pattern and Polymorphism - Two Birds With One Stone

First off, many thanks to the people that messaged me saying they enjoyed the Observer Pattern tutorial for saving composites. I'm really glad it helped demystify patterns and OO for you! And can I add it was pretty cool that these people were a mix from both CF and C# camps :) I am a big advocate of design patterns so I will not be shutting up about them anytime soon, sorry!


Today I wanted to touch on my favorite behavioral pattern - the Strategy Pattern.



What does this pattern solve? If you have an object that:
  1. Is constantly being subclassed just to override specific implementations (methods).
  2. Has a fugly method(s) that has to run through a mine field of if/case statements just to execute.
Let me start of by first saying when you use this pattern you can truly see polymorphism at its finest. And yes, polymorphism is achievable through ColdFusion. If someone tells you different, they are smoking crack. Say no to drugs.


I won't bore you with the computer science explanation of polymorphism so here's my simple, to-the-point summation. If objects are of different types but share a common derivative(s), they should be interchangable. This should help, lets keep it simple:
If A = B and A = C then B = C.
In the example above, B and C can behave polymorphically because they both share a commonality - they both are a derivation/implementation of A. Understand this critical principle of OO and you are well on your way to your OO awakening.


Moving on. Here's the official definition of the Strategy Pattern:
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
What's this mean? I've underlined the key words in this definition. When you hear algorithms, think verbs which mean methods. When you hear encapsulate, think about delegating responsibilities to new objects whose sole purpose is to protect "something" and ensure that these objects are doing only  "something" and doing it well (SRP principle). And lastly, when you hear interchangeable think polymorphism. I'll reword that definition a bit to be in line with what I just described.
Define a family of methods and make sure you've encapsulated each distinct method implementation (algorithm) away into new objects so these objects may be switched out polymorphically at run time.
First, here's an abstract class diagram I put together:




For the purposes of this tuorial just imagine that the inside of the legacy code for subject.doStrategy() you've got the if/case problem going on. It's riddled with them. 
e.g. -if something, set this then return this way, if something else set that and return that way. You get the idea. 

In the class diagram above notice:
  • We have a Subject, think of this as concrete class that has a composite relationship to an implementation of IStrategyBehavior which subject.doStrategy() delegates to. 
  • The idea is that subject.doStrategy() should NOT have any switches or ifs or overridden via subclassing just to provide concrete implementation.
  • Also notice the UML note. We delegate to another object that implements IStrategyBehavior through composition (Subject is composed of IStrategyBehavior), this is where that whole "encapsulate each one, and make them interchangeable" is happening. We're saying listen, Subject, yes you will doStrategy() however you are delegating to a "strategy" property and ask it to call its own doStrategy(). This means at runtime you, Subject, don't care about how your are doStrategy()'ing, you're handing it of to your polymorphic property "strategy" and calling doStrategy() on that. We've just encapsulated that behavior away from Subject into its own set of classes or family of algorithms.
OK now we can move on to concrete designing but first lets understand our requirements We're going to use the standard gaming example well because COD:Modern Warfare 2 just came out and it's glorious. (xbox live : Error401, add me)
  • Build a Soldier that can fight() 
  • fight() cannot have any if's/switches inside of it
  • fight() will execute differently based off what weapon Soldier is holding - Pistol, Rocket Launcher and Machine Gun
Here is the concrete design:


Here notice the following:
  • We've modeled a Soldier (Subject) and are creating a strategy pattern for subject.fight().
  • Interface ISolder has a fight() method with the same contract for for fight() in IFightBehavior. 
  • We've created three concrete implementations (concrete strategies) of IFightBehavior - PistolBehavior, MachinegunBehavior and RocketLauncherBehavior.
  • Side note : This pattern can also be achieved through an abstract class and subclassing but try to stay away from inheritance as much as you can. Favor composition and interface based programming. You have more flexibility that way.
Code time - Soldier.cfc (I've omitted showing the interfaces. It should be straight forward how to create them from the class diagram above):

  • Critical piece of code on line 2 - fightBehavior is polymorphic. Interface based programming baby. In most of my examples I will program to an interface, not an implementation (concrete type). At run time this property can be switched out by  any object that implements IFightBehavior. That's polymorphism peeps. Simple as that. Don't over complicate it.
  • fight() delegates to the fightBehavior property. In the abstract portion of this entry, when I said that a Subject, in this case Soldier, doesn't care how it does doStrategy(), in this case fight() - this is exactly what I mean. You're simply calling getFightBehavior().fight(). Soldier doesn't care how its actually doing it because it could be doing it n number of ways through polymorphism. All Soldier cares about is that fight() returns a string. And we the developer have ensured that it will since fightBehavior implements the IFightBehavior interface which also has a matching fight() method that expects a return of string.
Here is what your concrete behaviors might look like. I've consolidated it into one image in order - PistolBehavior (goes Bang!), MachineGunBehavior(lots of bangs!) and RocketLauncherBehavior(goes Boom!):


  • The classes simply implement IFightBehavior and provide a concrete implementation for fight() in their own unique way. This is their only responsibility.
  • These three classes are now interchangeable with any return type, property or method argument with the type of IFightBehavior. Like where? Like in Soldier's fightBehavior property!
And finally the implementation and the results when run:


  • While I'm hard coding the actual setFightBehavior() above, let's imagine that the setting of the weapon came from a weapon select box from a form. When the form is posted you process the selections and dynamically set the behavior and subsequently call soldier.fight().
  • If you added 50 more weapons to the select box as long as there were 50 more behavioral classes that implemented IFightBehavior, your consuming code DOES NOT CHANGE and you don't have to needlessly subclass and override methods anymore. That's the beauty and power of this pattern - polymorphism at runtime which keeps your API clean. In our example above, calls to solder.fight() need not change.

To drive it home for the last time. A solution without this pattern would most likely force you to add additional subclasses or add if/case's in solder.fight() that checks for weapon a soldier is holding, do some process logic then execute fight() in a specific fashion. Unnecessary complexity.

So there you have it - the strategy pattern in a nutshell. I've managed to work out some pretty slick solutions with this pattern in CF and C# from organizing complex search algorithms to encapsulating gnarly business logic and I'm sure you could find umpteen more uses for it. This pattern keeps complexity low, forces objects to be highly focused with singular responsibility all while being easily scalable and maintainable.


Hopefully I helped you get another step closer to object oriented design pattern nirvana. And remember, as John Whish said to me and I couldn't agree more, "Death to inheritance - long live the strategy pattern!"

-Mick


p.s. - which pattern is next?

Monday, December 7, 2009

Using Observer Pattern to Save Composite Objects

I've recently been asked by a few people how I  go about saving composite objects so I figured it may be worth a blog entry.  I have a handful of applications I work on, both in CF and .NET, that share similar patterns for solving the problem of saving an object and its composites transactionally and in order. For example, if Object A has an Object B through composition and B cannot save until A is finished saving itself. Additionally if B has a problem saving itself, A should rollback as well. So how the frick does that all work?


As with really anything in programming it can be done a million ways. Some ways are better than others but there is no golden ticket. In my experience, an acceptable solution could be by way of a service object, business object, DAO, ORM (no brainer here) or a combination of all of the above depending on how abstract you want to get. In this example I'll walk you through how I solved this problem using the tried and true Observer Design Pattern.


The Observer Pattern was created by the Gang of Four (GoF) - Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Long story short back in 95' these very smart guys identified 23 recurring software problems, designed solutions for each called a "design pattern" (which borrowed from proper architect Chris Alexander) and released a book to school us on it. If you don't own this book you definitely should, shame on you. Familiarize yourself with the problems and patterns the GoF describe and lock it away in your brain cache. When time comes, you'll be well equipped to tackle some tough design problem thrown at you. Cool? Good. Here is the definition of the observer pattern:
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
So given the composite save problem at hand, I looked to design patterns to see if I could use one as a viable solution. I think I was on a long flight to Boston so I spent the entire flight sifting through design patterns and with the help of the book and a beer or two, I was pretty sure the observer pattern would be a good match. Thinking of the problem at a high level we have a possible one-to-many relationship between an object and its composites that needed behavior between the two such that when an object is saved, it needs to notify the composite objects who are waiting intently to save themselves but cannot before that initial object is saved successfully. For the purposes on this blog entry I've created a couple class diagrams that explain the design, then we'll jump into some code. Here's the high level/abstract:




[click to view full image]

You'll see that there are two interfaces involved in this implementation:






  • IObservable - Any class that needs to be observable must implement this interface. It contains methods to attach() as a observer, detach() an observer and to notify() its observers of an event. Simply put, if you think of a pub/sub technique, an object implementing IObservable will be the publisher.
  • IObserver - Any class that can attach itself as an observer to an an object that implements IObservable must implement this interface. It has the API that IObservable will call when it notifies its observers.

Now here is the UML with concrete implementations:




[click image for full view]

Looking at this diagram you should be able to discern the following:






  • We have a Person that has an Address - here's our composite relationship.
  • It's a one to many relationship. A Person can have multiple Addresses.
  • Private member - observers - is of type IObservable[]. Whats the brackets for?
  • There is a save() method is each object. To keep things simple just imagine that save() is delegating to a DAO or using some snazzy ActiveRecord pattern to actual persist the record. Bottom line is don't worry about it for this example.
  • Person is observable by way of implementing IObservable. That little symbol with the interface name above each class is called a "lollipop", no joke. It means this class means  "implements" whatever name is shown.
  • Address is an observer by implementing IObserver.
  • addEmail() has an optional second parameter "attachAsObserver" that is default = true. Remember this, you'll see how this plays into a clean API when using this pattern.

A concrete implementation of Person might look like this:


[click image for full view]
You'll notice that:









  • attach() will push an observer into the private subscribers array. This method is responsible for allowing observers subscribe to it (Person).
  • notify() loops over all observers and invokes update("some_event") on the observer that tells the observer, "hey dude, just fired "some_event". In the same breathe it is also inspecting the result of notify() that sniffs for a return of false which means something went wrong so return false and stop notify execution. I know that there are possible scenarios where you would not want to stop processing and move along but exploring that is out of the scope of this example. Although if you go ahead and re-factor this example to do this I'd love to see it!
  • We've overridden addAddress() which manages our address collection. This is essentially the setter for Address but called it add instead of set because of the one-to-many relationship. Notice that the we've added a second parameter to this method called attachAsSubscriber that has a default = true so its optional. In the logic, we're saying if attasAsObserver is true then automatically attach the Address as an observer to Person.
  • save() calls super.save(). Make believe Person extends some base persistence object.
K - on to some Address code:


[click image for full view]
Here you'll notice that:
  • update() will receive the event message and process accordingly. Please do not confuse this with a CRUD operation. As a best practice in you database layer there really shouldn't even be a method called update(), rather call it save(). Anyway, what update() will do is inspect the event that was just fired by IObservable and if it is a "save" event, then return save(). This is a very important piece right here! The return of save() gets bubbled up to the observer. This will ultimately determine the rollback which we'll get to in a second. You can optionally override Address.save() also do the same algorithm as Person.save() if Address also needed to be observable and notify its own observers.But thats up to your requirements. Just want to drive the point home that pattern can be implemented n-levels deep. D observes C who observes B who observes A. A = mack daddy.
  • update() by default will return true.
  • update() uses the eventInfo structs observable key to get the Person that was just persisted. This allows Address to save() correctly. I chose to go with a struct for eventInfo because an event might be fired in the future that needed to pass a handful of additional info to its observers. This allows for future flexibility.
And finally here is how you might possibly consume this API in its entirety. For the purposes of this example its in a cfm page but you'd probably want it somewhere in a persistence layer - perhaps a controller/service type object.


[click image for full view]


Here we have:
  • Created a new Person and Address.
  • Associated the Address to the Person. Under the hood this will also attach Address as an observer to Person but the API remains simple. Looks just like a simple setter - addAddress(in Address). It has no idea the observer pattern is going on in the background.
  • We are simply making the entire save process transactional and when an observer fails to save or an exception is thrown, we handle it accordingly by rolling everything back. You could have 100 objects in the queue to be save()'d, fail on save() #99 and it'll nicely rollback everything for you.
  • And now since we've kept the Observer pattern extremely generic, you can feel free to leverage this implementation for any other event, not just saving.
There you have it. Observer pattern goodness that could hopefully make the GoF a little proud. I've implemented this technique both in .NET and CF and it works great for me. For my C# peeps, Microsoft is releasing an set of Observer Pattern interfaces that will support this type of usage out of the box. Check it out here.


So in closing, this is just one technique of many so I encourage you to explore some other ways and please point me to ways you've done it in the past. I'm interested in seeing it!


Apologies in advance for any bugs in this code. Everything was typed on the fly but hopefully you get the idea.


Till next time my friends.

-Mick



Resources:
Design Patterns with C#
Christopher Alexander, founder of the design pattern in architecture.
GoFyou owe it to them so buy their book