Você está na página 1de 10

10,609,275 members (42,642 online) 343 Sign out

1 srinivasan.mca
home quick answers discussions features community help Search f or articles, questions, tips
Articles General Programming Programming Tips Design and Strategy

Article
Browse Code
Bugs / Suggestions
Stats
Revisions (15)
Alternatives
Comments (15)
Add your own
alternative version
Share
About Article
A good architecture means
money saved in learning,
maintaining, testing, fixing,
extending and scaling
source code. The following
guidelines are not
exhaustive and are meant to
be applied on top of the
SOLID principles and proper
use of OO Design Patterns.
Type Article
Licence CPOL
First Posted 3 May 2014
Views 17,751
Bookmarked 80 times
Architect Dev Design
Intermediate Patterns , +

Next
Rate:
10 Golden Rules Of Good OOP
By Giovanni Scerra, 8 May 2014
Is your email address OK? You are signed up for our newsletters but your email address is
either unconfirmed, or has not been reconfirmed in a long time. Please click here to have a
confirmation email sent so we can confirm your email address and start sending you
newsletters again. Alternatively, you can update your subscriptions.
Introduction
A good architecture means money saved in learning, maintaining, testing, fixing, extending and scaling
source code. This requires more time and care for the initial development, but quickly returns the
investment with great interests.
Inevitably, even the best designed architectures need some adjustment and refactoring in time.
Patches, modifications and last minute changes are part of the IT business, therefore, quick and dirty
solutions, when needed, should always try to confine themselves in an isolated place where it will be easy
to refactor later on, and that will affect as minimum as possible all the other components.
The following guidelines are not exhaustive and are meant to be applied on top of the SOLID principles and
4.93 (61 votes)
articles
Top News
The Secret Messages Inside
Chinese URLs
Get the Insider News free each
proper use of OO Design Patterns.
1. Avoid Almighty Classes, Use Meaningful
Names
When designing a new class or refactoring an existing one, a developer should make a list of all of the tasks
performed by that class and come up with a name that easily and shortly represents what the class does.
If the class does too many things, a representative name is usually something monstrous like:
PageInfoBuilderAndConfigurationLoaderAndLinkAnalizerAndCacheManager. That means the
class has too many responsibilities and should be broken into multiple components, ideally, with one
responsibility each.
When using OO Design Pattern, it is much easier come up with meaningful names, since the design pattern
itself frequently identifies the type of responsibility.
For example:
If the class creates objects that use the suffix Factory or Builder
If the class is responsible for coordination and communication between other business classes, then
use the suffix Mediator or Faade
If the class is used to control the use of a resource class, then Proxy would be a good suffix
If a class is wrapping another in other to adjust its use for a consumer class, then use the Adapter
suffix
2. Avoid Too Many If or Switch Statements
Excessive conditional logic makes the head of the developer spin like Regan in The Exorcist. Things become
uglier when the same conditional logic is applied in many places in the application. It means that different
business behaviors are managed with ifs and switches instead of being properly managed using
inheritance or composition, separating the different behaviors into different implementations of a common
abstract class or interface. For instance, there is a configuration variable that is telling if data should be
stored in a database or in a file. Then everywhere in the code wherever we have to save something, we use
the conditional logic:
Collapse | Copy Code
If (ConfigParameters.SaveTo == StorageType.DataBase)
morning.
Related Videos
Related Articles
The Golden Rule of
Programming
How to interpret complex
C/C++ declarations
An Event Based Rules Engine
Four ways of passing data
between layers
Introduction to Object Oriented
Programming (OOP)
IDEAL CHALK - mnemonic
acronym and mnemonic
images for Object Oriented
Principles (OOP)
11 important database
designing rules which I follow
Generation of n Digits of the
Golden Ratio (Phi)
Introduction to Object Oriented
Programming Concepts (OOP)
and More
Violating Liskov Substitution
[ Save something in the db]
Else
[ Save something to file]
Imagine that we need to add a different behavior (for instance, save to a distributed cache): now we have
to extend the conditional logic everywhere in the code. Imagine then, multiple configuration variables
determining multiple behaviors and leading to a jungle of unmaintainable and intricate conditional
statements.
The way to go, in the above example, should be:
1. Create an abstract class or interface representing a Storage object (e.g., IStorage), with save
and load methods.
2. Create different implementations if the abstraction such as DBStorage, FileStorage,
CacheStorage, etc.
3. Create a factory that will instantiate the right storage implementation based on configuration and
return it as abstraction (IStorage)
4. When in need of saving data just write: myStorage.SaveSomething() where myStorage is a
IStorage variable created through the factory
Sure, its a lot of work but still much less work than maintaining conditional jungles. It also has one big
advantage that is explained in the following guideline.
Developers that are abusing conditional logic are digging their own graves, and usually end up being
overstressed.
3. Use Seams: Extending Is Better Than Changing
A seam is an area of source code where the behavior can be changed without editing the code. Seams
leverage flexibility and they are great for one simple reason: They encourage extending the architecture
instead of changing it.
Why is extending better than changing? Pretend that you are a new developer and you are asked to add
new storage options to an application.
Would you prefer to go through the code and change it with the new options wherever something is
saved? Finding your way around the code that someone else has written, that you do not fully understand
yet, risking of breaking something or missing one of the hundred places that need to be changed?
Or would you rather create a new class that you will write from scratch that would implement the
IStorage interface and be done without even looking at someone elses code, risk free?
Principle (LSP)
Challenges and solutions -
Architecture of a Modern Web
Application - Part 1
Fractals in theory and practice
Numerical Integration with
Simpson's Rule
Delegates Are Good, and Good
For You
MVP-VM (Model View Presenter
- ViewModel) with Data Binding
and BackgroundWorker in
Windows Forms applications
The "Rule-O-Nator" - An
example of dynamically loading
classes at runtime
An Overview on Many Terms
and Principles
OOP Manifesto on Best
Practices, Basic Rules & General
Ideas
Easily Create Your Own Parser
A Look At What's Wrong With
Objects
Related Research
Protecting Your Business Data:
Five Dos and Donts
It is not a surprise if the degree of confidence of a developer is higher in this last case.
4. Avoiding Global State and Non-Deterministic
Behaviors
COBOL developers in the 80s knew already that abusing global variables is a bad, bad practice. Global
variables are rarely justified, and their damning side effects can lead to mental insanity. That wise old rule is
still extremely valuable also in modern object-oriented languages, such as C#.
One of the many issues is that they create a global state of the application that compromises the
deterministic behavior of functions/methods; in other words, calling the same function twice with the same
parameters can give completely different results. Hence, the code is fragile, difficult to debug, and extremely
difficult to run on multiple threads.
Use of any sort global variables/objects is therefore highly discouraged.
Ideally a good architecture is stateless. Practically speaking though, almost every architecture need some
sort of state (e.g. a database, files, and etcetera).
The solution is state isolation and consists in two simple rules:
1. Keep the life and scope of a state as short as possible (which means, for instance, that even the class
members should also be contained).
2. Isolate the state by wrapping into a separate layer that presents itself through abstractions (base
classes or interfaces).
For instance, a database can present data in the form of business entities through a well-defined interface
of a Data Access Layer. The isolation will make life much easier when it comes to testing or troubleshooting
a problem.
5. Avoid Static Classes for Helpers, Utilities & C.
Developers are frequently tempted to create static classes, especially when it comes to helpers, utilities
and so on. The reason why a developer resorts to a static class is not much different from the reason a
COBOL developer would use a global variable in his code: it gives immediate access to data from anywhere
in the code, no effort needed.
Unfortunately, static classes act frequently as a global state and create the non-determinism that should
Fine-Tuning the Engines of SMB
Growth: 4 strategies for growing
your business
be avoided. But it gets worse. Since static classes can be used everywhere in the code without being
passed explicitly as parameters, they create secret dependencies that are not revealed by the API
documentation. The code behavior then becomes less and less declarative and both the clarity and
maintainability of the code drops dramatically. Static classes come with global variables and
dependencies creating tight coupling in all the consumers (coupling is transitive).
Last, but not least, code using static classes is not testable in isolation, making unit testing a nightmare.
Unless strictly needed for performance reasons, the use of static classes should be avoided. Static
variables are still OK for constant objects (although a static property without setter would be better) or
to hold private references to objects inside factory classes.
6. Separate Creational Logic from Business Logic
One of main principles of a good architectural design is that the logic of creating an object and the
business logic (what the object really does) are two different concerns that should be kept as far apart as
possible.
The creation of objects is a concern that belongs to specialized classes, such us factories or builders. They
only and exclusively should have the monopoly of creating objects.
The objects, on the other side, should only be concerned about performing business logic (ideally, a single
business concern only) and not to worry about creating other objects (dependencies).
For instance, an object called HTMLAnalyzer (designed to analyze HTML links) needs a LinkAnalyzer in
order to work. That means it depends upon the LinkAnalyzer class and therefore, an abstraction of
LinkAnalyzer should be explicitly passed as parameter in the constructor of HTMLAnalyzer or as
parameter of the methods that are using it. A developer may instead think of creating the LinkAnalyzer
inside the HTMLAnalyzer using the new statement.
Double mistake:
First of all, the new statement creates a dependency to a specific type (no abstraction); therefore, the
behavior is carved in stone and impossible to change without major refactoring.
To test HTMLAnalyzer in isolation is now impossible because we will have to test LinkAnalyzer
at the same time.
The correct way is to use dependency injection to explicitly and declaratively inject all the needed
dependencies when and where they are needed, for instance, in the constructor.
In this case, the object that uses a HTMLAnalyzer will invoke a factory to obtain an abstraction (interface
or base class) of a LinkAnalyzer object and inject it into the HTMLAnalyzer constructor.
At any time, the behavior of LinkAnalyzer can be changed by creating alternative implementations and
the only change required will be in the factory, not in the actual code of the business logic where changes
are expensive and dangerous.
One simple implication of this is that the Singleton design pattern is intrinsically wrong and should never
be used. It mixes in itself the creational logic (it creates itself) and the business logic, not to mention that it
holds itself as static global object that it will never make it to the garbage collector (a singleton, like
love, is forever) and constitutes a nasty global state of which we already spoke.
Factories are the long, simple, easy to control and boring pieces of code where all the changes are simple.
Business objects contain all the magic tricks and complexity, so the less we change here, the less we break.
Keeping them separated will lead to less business logic modification and more business logic extension.
7. Keep Constructors Out Of Business
As a corollary of the previous point, business logic should never be coded into constructors of an
object. The purpose of constructors is only to assign some properties, initialize variables and eventually
hook up events. If constructors are actively doing something business-related, then, we will never be able
to separate the creational logic from the business, and the architecture will always be messy.
8. Law of Demeter
This principle is mostly unknown among developers and, of course, one of the most useful. Without going
into formal definitions, the principle states that a class should depend only and strictly upon what it is
using; therefore, only what is really used should be injected into a class.
A great explanation that I have found in a Google TechTalk (http://www.youtube.com/watch?
v=RlfLCWKxHJ0) is the following:
In an eCommerce system, each user has a Wallet object containing a collection of CreditCard objects
and other financial information. The class responsible for the online payment has a method like this:
Collapse | Copy Code
bool Pay(Wallet wallet, string ccNumber, double amount) {
CreditCard cCard = wallet.GetCreditCards().GetCard(ccNumber);
return ProcessPayment(cCard, amount);
}
How would you consider a payment method like this? When you are paying for your new shirt at Macys,
would you give your entire wallet full of credit cards and cash to the cashier and let him/her pick the right
Credit Card or bills from it? Of course not. So if a payment class needs only a CreditCard object, a
CreditCard shall be given--nothing more, nothing less. No need to create a dependency by passing a
huge container object (the Wallet) full of unnecessary objects that creates tight coupling and security risks.
Container objects (usually named with vague suffixes like Container, Context, Service or
ServiceLocator, Portal, Environment, etc.) should not be passed as parameters of constructors or
methods (unless the whole content is needed).
9. Keep Complexity Level Low
The innermost level of nested loops, switches and ifs is the measure of how complex is a method. For
instance, if inside a foreach loop inside another foreach loop is scored as a complexity of 3. The
complexity of a method is the maximum complexity of its code and should never be above 4. If it is more
than 4, then it is refactoring time!
10. No Long Methods
As a rule of thumb, every method should fit in a screen without vertical scrolling. If it does not, it is time to
refactor. Refactoring long methods will make a developer very appreciative about the absence of global
variables.
References
Martin Fowler
Refactoring - Improving the Design of Existing Code
http://martinfowler.com/books/refactoring.html
Misko Hevery
Clean Code - Google Tech Talks
http://www.youtube.com/watch?v=RlfLCWKxHJ0
Robert C. Martin
Clean Code: A Handbook of Agile Software Craftsmanship
http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882
Dominic Betts, Grigori Melnik, Fernando Simonazzi, Mani Subramanian
Dependency Injection with Unity
http://www.microsoft.com/en-us/download/details.aspx?id=39944
Chad Parry
Do-It-Yourself Dependency Injection
http://blacksheep.parry.org/wp-content/uploads/2010/03/DIY-DI.pdf
Kent Beck
Test Driven Development: By Example
http://www.barnesandnoble.com/w/test-driven-development-kent-beck/1111349772?
ean=9780321146533
Erich Gamma, Richard Helm, Richard Helm, John Vlissides
Design Patterns: Elements of Reusable Object-Oriented Software
http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612
License
This article, along with any associated source code and files, is licensed under The Code Project Open
License (CPOL)
About the Author
Giovanni Scerra
Technical Lead
United States
Technical Director at AFS Technologies, New York, NY

More info about my skills, projects, presentations, etc.:
http://www.linkedin.com/pub/giovanni-scerra/10/940/348
Follow on LinkedIn
Add a Comment or Question
Search this forum Go

Article Top
Comments and Discussions
Profile popups Spacing Relaxed Noise Medium Layout Normal Per page 25 Update
First Prev Next
Rahul Dhammy
14-May-14 1:41
Prasad Khandekar
13-May-14 6:38
Sumanth Madireddy
11-May-14 22:12
T B Trung
9-May-14 22:53
yalenap
9-May-14 17:02
Very Nice-A vote of 5.
My vote of 5
Excellent Article
My vote of 5
missing one rule
Permalink | Advertise | Privacy | Mobile
Web02 | 2.8.140510.1 | Last Updated 8 May 2014
Article Copyright 2014 by Giovanni Scerra
Everything else Copyright CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid
Giovanni Scerra
9-May-14 17:38
yalenap
9-May-14 17:53
Member 10022530
9-May-14 12:07
Giovanni Scerra
9-May-14 17:33
Wierdguy4575
6-May-14 19:00
Mike Riley - QUSA
6-May-14 15:06
Giovanni Scerra
7-May-14 6:09
darshan joshi
5-May-14 1:58
Member 4676641
3-May-14 11:01
PIEBALDconsult
3-May-14 17:05
Last Visit: 15-May-14 7:49 Last Update: 15-May-14 4:03 Refresh 1
General News Suggestion Question Bug Answer Joke Rant Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.
Re: missing one rule
Re: missing one rule
a nice list
Re: a nice list
Nice list
A good set of 10
Re: A good set of 10
My vote of 4
Excellent content and dissussion
Re: Excellent content and dissussion

Você também pode gostar