AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=871&pId=-1
Design Patterns in VB.NET
page
by David Simmonds
Feedback
Average Rating: 
Views (Total / Last 10 Days): 27456/ 23

What are GoF Design Patterns

Design patterns represent repeatable solutions to common problems found in designing object oriented systems.  They represent optimal (though not perfect) ways of arranging classes and objects together and assigning to each class responsibility for instantiating, interacting with, composing other classes and passing messages to each other in order to solve a particular problem faced by developers.  Consider a radiator, such as is found in a car.  You might ignore it for a long time, but if you have a “vintage” automobile, then driving on a hot day in bumper-to-bumper traffic (a radiator’s worst nightmare) you might notice your windshield getting an unscheduled “steam-bath.”  So, you call your favourite mechanic and you get the car to his garage.  Then, having a lot of time on your hands, you finally pay some attention to the unassuming, but very vital piece of equipment that thanklessly cools your car engine day by day.

The radiator is a vital component in your car and is composed of parts which interact to achieve the cooling of your car’s engine.  In the same way, a design pattern represents an arrangement of classes with an assignment of responsibility to each, which represents the way to solve a particular problem. This arrangement is easily recognizable to the initiated and is easily reproducible in other situations.  It is also easy to extend and maintain the pattern by those who understand its inner workings.  Every self-respecting mathematician understands that math problems have patterns which comprise participants that collaborate (quadratic equations have cubes and squares that are solved by factoring, simultaneous equations have two equations, each with two instances of the variables to solve for and geometry problems have lines and angles and are solved with cosines, sines and tangents). The goal of this tutorial is to have every single O-O programmer in the world be initiated into the structure and workings of design-patterns.

 

Origin of Design Patterns

There are hundreds of design patterns that are broken down broadly into design patterns, concurrency patterns, transactional patterns, web patterns and other categories.  So design patterns are by no means the only category of patterns.  No one is saying that the Gang of Four (GoF) have cornered the pattern market.  For that matter, it should be pointed out that the GoF did not even create these patterns.  They largely existed in O-O systems before.

In 1994, four guys, now all PhDs: Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides (who died of cancer November 24, 2005) came together in order to write a book that was built on Erich’s PhD thesis.  It was published as “Design Patterns, Elements of Reusable Object-Oriented Software” (Addison-Wesley, ISBN 0-201-63361-2).  On the way to being published, it was presented to several O-O conferences and eventually in book form became known as the “bible” of patterns.  The software development community also affectionately nicknamed these four pioneers the “Gang of Four” or GoF (folks concerned about misguided religious fervor should note the common “o”).  So we have a “Gang” which is responsible for a “bible.”

The GoF patterns represent the core of design patterns.  When you study them and look at other patterns, you see tremendous similarities which make you suspect that you could get by with focusing on the 23 they described.  They might have tweaked them a bit to improve their robustness, but they were pre-existing and the GOF simply documented them.  In fact, a requirement for a pattern being documented was that it already existed in several applications.  What the GoF actually contributed was their methodology for cataloging them, which is impeccable and introduced the well needed thought process required to mull over, digest and ruminate on design patterns in order to make them a part of your vocabulary and incorporate them as the centerpieces of your design efforts.

So the GoF simply did for design patterns what the Bauxite companies did for Aluminum. The Bauxite companies did not make Alumina, but they engineered ways to find it, unearth it, process it, purify it and turn it into Aluminum - a metal that the GoF would have appreciated on (I assume) their trips across continents, since it is used in the instantiation of the structures of Airplanes and Cars due to its lightweight coupled with its tendency to behave very robustly under pressure.  The unfortunate thing is that scientists are usually poor businessmen (literally) and usually lack branding and marketing skills.  I ask myself what the difference between CISCO and what GoF Patterns could have been or still could be?  The answer I come up with is nothing.  In fact, let me be (one of?) the first to predict that there will be GoF certification in the future.  GoF centers will be all over the world, complete with logo and quarterly results.  I know PhD's abhor money.  But after all, the Bauxite companies did not create bauxite either.  And believe me, in Jamaica, when you work for a bauxite company, you do not exactly beg for a living.

As for the other design patterns and other categories of patterns, if you are interested in the hundreds that exist then do not stop here, you are welcome to go explore.  Me, I specialize in GoF.

 

Parts of a pattern

Before we can go into the parts of individual patterns, it should be mentioned that the GoF categorized the 23 patterns into Creational, Structural and Behavioral patterns.  This helps us to think of patterns among their peers, whether they instantiate classes for us, arrange the classes into useful structures for us or help the classes to collaborate at run-time in the most useful way.  This allows us to categorize our thinking broadly.

The GoF break-down each pattern into 4 major parts.

1) As the GoF point out, the design patterns are described in terms of their name, which allows us to get a quick handle on the general structure and interaction involved.  This is similar to automobiles where, instead of talking about “the device for cooling water coming from the engine through the use of the electrically-powered air-turning implement,” we can simply talk about the “radiator” and “fan.”  So the name is very powerful since it lends itself to a natural association with a mental image of how the pattern operates and also suggests how the pattern is internally arranged.  As they also point out, the names provide us with a common vocabulary for discussing the patterns.

2) The problem is similar to a description of the problem for which an auto part is used to solve.  For example, brake-pads are required for providing the functionality of stopping and radiators are useful for cooling the engine due to the heat given off.  Electrical relays help us to deliver current in a manner which isolates the switch circuit from the circuit which actually does work (such as a horn or starter), so that we reduce the risk of being shocked or setting fire to the car due to an electrical short circuit.  Similarly, the design pattern problem is a scenario that requires a methodical approach in which the interaction of the classes in the pattern provides functionality that addresses that problem.

3) The solution helps us to understand how the classes and objects in the pattern instantiate, compose, interact with and pass messages to each other in order to solve the problems faced in the particular circumstances.

Let us revisit the radiator.  A radiator usually has pipes leading hot water to it and cold water out of it, tubes for leading the water up through fins.  These fins provide a greater surface area for the fan to blow air through in order to cool the hot-water.  Not terribly different from the setup of the CPU-fan/heat-sink in your PC, the arrangement and internal interactions of the radiator represent a solution to the problem of heat exchange.  In fact, heat transfer systems everywhere have come to use this elegant arrangement of collaborators; this includes A/Cs, fridges, radiators, heat-exchangers in Power stations, etc. It just goes to prove that once a solution to a known problem has been designed in a simple, cost-effective, recognizable and reproducible way, people will tend to learn and implement it without fail (hence the adage – “Don’t reinvent the wheel”).

Now just as the radiator:

·         Is an arrangement of smaller parts (which):

·         Carry out responsibilities in sequence (hot water pipe, tubes, fins, cold-water-pipe)

·         Communicate messages to each other (using water)

·         Activate other components (cause the fan to spin)

·         Instantiate other objects (Check Engine warning)

 

Similarly, the classes and objects in a design pattern all have a responsibility for acting out the solution to the problem in the OO design space.

 

4) Consequences help us to understand the costs involved in implementing the pattern so that we can avert as much of the cost as possible without compromising pattern-safety.  Picture a relay that helps us to isolate high-current circuits from circuits, which the driver may operate through a switch interface.  While the relay is very useful, the fact is that the relay is an extra circuit to wire, an extra component to fail, more space taken up in the engine bay, another circuit to maintain and generally represents extra cost to the owner. The consequences help us to understand how patterns affect the cost of building the software, the performance impact and how to minimize them, etc.  It also describes how the different trade-offs affect the extensibility we eventually achieve.  Therefore in engineering terms, it shows how we can optimize the interactions of the classes in the pattern.

 

GoF Principles to code by

Design patterns did not just appear out of thin air.  They evolved from the application of certain design goals, which are widely known throughout the O-O community and which are taught along with almost every O-O language.  These include low-coupling, encapsulation and high-coherence.  You are expected to know these already.  To this the GoF have added certain higher-order design principles which help us to understand how the patterns came about and use them to test our application of the patterns.

Interface inheritance versus Implementation inheritance

The GoF hate inheritance so much that you would think one of them inherited a dog that bit him.  OK, so that is a bad attempt at humor, but the point is they really abhor class inheritance and with very good reason.  The truth is that any person who grew up in a Ghetto, but rejected the values generally passed on in those circumstances, would understand why class inheritance can be a terrible thing.  It locks in the behavior that your class is based on the parent class it inherits from.  Inheriting from a super-class is like getting from your behaviors hard-coded into you through the passing on of your parents’ genes.  It is hard to override inappropriate behaviors.

This, obviously, is why you have Interfaces in VB along with a compromise between class and interface inheritance: Abstract (MustInherit) Classes, along with a plethora of ways to inherit methods (mustoverride, overridable, notoverridable and shadows) in VB.  In social terms, Interfaces give you principles to live by.  These help you to figure out how to respond to life on a situation by situation basis, while parent inheritance means that you are limited to behaving the way your parents’ genes dictate.

Let us look at everyone’s favourite issue, money.  As responsible adults, we all have to earn a salary which allows us to care for our families.  However, we also need to ensure that the money we have stretches until more comes in.

We can declare an interface for a fiscally responsible person:

Interface GoodProvider
    Function EarnMoney ()
    Function ManageMoneyWisely()
End Interface

 

Your parents might have implemented it as:

' Parents interpretation of fiscal responsibility
Class YourParent
    Implements GoodProvider
    Function ToiledInTheField () Implements GoodProvider.EarnMoney
        MessageBox.Show("I go to my farm and plant and reap to sell at market")
    End Function
    Function KeepMoneySafe() Implements GoodProvider.ManageMoneyWisely
        MessageBox.Show("I keep my money under the mattress")
    End Function
End Class

 

But times change and so your implementation might be:

 

' You adapt to the new circumstances while fulfilling the same requirements:
Class ModernPerson
    Implements GoodProvider
    Function ExcelAtCareer() Implements GoodProvider.EarnMoney
        MessageBox.Show("I develop software using design patterns!")
    End Function
    Function InvestAstutely() Implements GoodProvider.ManageSalaryWisely
        MessageBox.Show("I talk to my stock broker regularly")
    End Function
End Class

 

Education generalizes our career options and allows us to earn a salary in any one of thousands of ways, some of which are more suited to our tastes, styles, abilities and makeup than others.  By specifying the required interface which we need to implement and allowing the class which implements the functionality to worry about how to get it done, we encapsulate that knowledge and allow for loose coupling between your classes and the clients which use them (in this case your children).

On the other hand, if we were to use class inheritance in this case, we would be locked into the behaviour of the parent class.  It would mean that if the parent class implemented the WorkedAtTheirJob using farming techniques, we similarly end up farming.  If we found it impossible to keep farming then we would be in trouble!  The problem is that left to the default situation, the default behaviour is carried through.  With interfaces and abstract classes, there is less default behaviour and so the natural thing is to figure out how to implement the functionality in each class.  It also means that we can interchange the implementing classes (so a child provided for by a stock broker can be just as well taken care of as a software developer’s child)

As the GoF point out, Inheritance breaks encapsulation since the behaviour and functionality of the subclass is tied to the ParentClass.

 

Object Composition instead of Inheritance

Think of Class inheritance versus Object composition in this way.  Suppose you got your car tires from the manufacturer, with the tire fixed inextricably to the rim.  This would be a problem and you have one solution: drive extremely slowly so the tread never wears out.  The better way is to make the wheel in a manner which allows it to be composed of a separate tire and rim.  If anything happens to one, you just change it and the other is still good to go.  The rim gets bent, you replace (or straighten) it.  If the tire gets worn, buy a new tire and have them put it on the rim.  The sweet thing is that it allows you to not only fix problems such as tire wear, but also enhance performance.  If you want a quieter or more fuel efficient roll, you can change the tire material; want better handling, go for lower profile tires.  Supposing you like the way your car looks but think the rims could lift the appearance, you can setup the car to look vastly different by changing the rims.  The cosmetic-object in this case is the rim and you can extend the look by changing it.

In an organization, object composition is what we do with departments and teams.  Instead of saying "I want someone to tune the database, manage security permissions for it, implement changes to table structures," we simply say “Get me a DBA.”  Throw in a couple of people who know how to write code (Programmers), someone who knows some advanced VB.NET (UML), and knows how to talk to users (Software-Engineers) then you have a development team.  The Bridge pattern, Adapter pattern and Command pattern and some other patterns rely on this principle of object composition.

Find what varies and encapsulate it

When you look at an automobile, you see that there is disposability built into the parts.  If they did not do it this way, you would have to change your whole engine when any major part wore out.  Cars would experience incredible plunges in resale value for every 10,000 or so miles they drive.  After 50,000 miles most cars would be absolutely un-maintainable.  What causes mechanical engineers to have to build in varying levels of disposability of parts?  

Well, there is an engineering principle that the more moving parts you have in a machine, the more susceptible it is to breakdown.  The moving parts wear against each other and fail.  The ordinary person sees this most in their shoes.  Thus, engineers build in changeable bearings, bushings and rubbers; parts which represent the interfaces between the moving parts and so bear the brunt of the wear.  Being mostly made of rubber, they are relatively inexpensive to change once the wear out.  You see this clearly in good software, except that instead of parts that literally wear against each other, we have code that wears against moving and changing user requirements.  Just as car makers build loose coupling into their car parts for interchangeability due to wear, we must build in interchangeability within our code.  We must determine what functionality requirement can vary over time, encapsulate it and instead of interacting with it directly, interact with it through an interface.  Many times it means making the structure of our code a bit more involved, but because we do it with recognizable design patterns, the developer who is grounded in patterns can almost instantly figure out the interactions within our classes and figure out which classes to change, substitute or add when a change in requirements comes about.  This is similar to a mechanic who knows which front end part to check and change when a tire wears unevenly.  And just as a mechanic knows which particular bolts to pull in order to change a front-end part without pulling the whole front-end, our software developer knows how the new class should be “bolted” into the other parts in our system.  So when a change is made, he can quickly access the code in the rest of the system which refers to that changed/substituted class in order to swing the updated or new class into use.

 

My contribution to the Design Principles: Separate Object models from Algorithms

The GoF did not mention this as a general design principle or theme.  They did mention it as a benefit of the Visitor pattern.  But looking at Composite, Iterator, Visitor and Interpreter, I see when it is worth generalizing it into a design principle because it is a recurrent theme running through those patterns.

The point is you should isolate the algorithms that you use on an object model from the object model itself as much as possible.  This may not seem clear right now, but when you look at the patterns mentioned above you will see the power of applying this principle.  You will be able to change object-hierarchies in a way which does not interfere with the operations that need to work on the components within that model. 

Incidentally, some folks will realize that the separation of object model from algorithms is very similar to the principle of encapsulating what varies, since in most cases the object model tends to be very stable while the algorithms will evolve over time.

 

Advantages of Design Patterns

Design patterns allow for extensibility in the O-O design, so that particular functionalities can be enhanced, debugged, modified, made secure or performance-tuned without breaking the rest of the surrounding code.  You can apply code access security to one object, while applying role based security to another; so if you have 3 security experts, you could split up their work on that basis.

They allow for a need-to-know approach to development (I bet they use them a lot at the CIA).  Suppose you have a secret routine with a lot of industry-intelligence in it and your company’s survival depends on it.  If a new programmer joins your company and has not yet earned a high level of trust, do you expose the new-programmer to the inner guts of the weather-prediction code or do you have him instantiate classes by an object-factory which is accessible only to your lead developers?  Creational patterns help you to implement solutions to this dilemma.

Design patterns also allow you to separate the Object-Model from the Operations which must be carried out on the structure representing the Object Model.  The Trinity of Composite, Iterator and Visitor patterns help you to achieve this.  Interpreter and Composite patterns also help to accomplish this by separating the grammar which describes a language from the algorithms that figure out the meaning of sequences of words in that language.  You can actually overhaul Object-Models extensively without touching a line of code which traverses and evaluates it!

Design patterns allow you to extend the functionality of classes defined in class libraries from third party developers, even if they are scoped in a way so as to disallow extension.  Supposing a function in a class library is marked as “not override ready,” you can still extend the operations by using the decorator or adapter patterns.  Obviously, you should be careful about the extension in these circumstances since the manufacturer must have had reasons for not allowing you to easily extend the functionality.

Without using design patterns, classes will multiply within your code in order to represent every possible combination of behaviors.  Either that or you will put in so many if-then-else case statements that you will choke on them the first time you try to unravel them.

Why should your clients, boss or customers care if you use design patterns?  Well it will help you to avoid the conundrum that is facing the automobile industry right now.  There is a need to move toward renewable sources of energy and so solar powered cars must take prominence in the future.  The problem lies in that trillions of dollars is invested in the current infrastructure which produces gas-powered cars.  The new technology is incompatible with the old and the introduction of the new will mean throwing out the old. This is what happens with badly designed software.  In order to introduce new changes you have to discard what exists since it is not extensible enough to permit changes that the client requires.  Design patterns help you to avoid this problem.

Design patterns also represent the natural boundaries for subsystems when designing and testing, since the sum of parts should interact in a predictable and interface-able manner.

It represents a predictable way of maintaining systems.  The very expensive nature of cars dictates that they be built on similar platforms for cost savings and also that they be maintainable based on a engineered similarity – 4 wheels, a steering wheel, front-end, water pump, ignition systems – that all follow a common model.  A mechanic who has never worked on a particular brand of car before is still able to perform many of the fixes on it because he knows what the general arrangement of parts will look like and what tends to cause the problems.  A Toyota specialist, once he understands the design patterns of a Toyota, can look at a fairly new model from Toyota and fix it.  Software must become like this.

Security can also be improved with the use of design patterns since the layer which interacts with databases can be abstracted out of the reach of the user-programmer who then has no need to dig around in the database to do their GUI work.

Conclusion

Design patterns are repeatable solutions to the problems faced my programmers today who work in object-oriented systems.  The more you know about them the more I know you will want to learn more. They offer endless possibilities to the computer world.

 

 

 

 

 


Product Spotlight
Product Spotlight 

©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-29 12:46:48 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search