antipatterns

21
Antipatterns - when over engineering solves problem you do not have. Studying Failures Might Be Even More Fruitful Than Studying Success. DESIGN PATTERN PAPERT LUCA ALIBERTI 1

Upload: luca-aliberti

Post on 18-Aug-2015

26 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: AntiPatterns

Antipatterns - when over engineering solves problem

you do not have.

Studying Failures Might Be Even More Fruitful Than

Studying Success.

DESIGN PATTERN PAPERT LUCA ALIBERTI !1

Page 2: AntiPatterns

LUCA ALIBERTI Winter 2015

DESIGN PATTERN PAPERT LUCA ALIBERTI !2

Page 3: AntiPatterns

PREFACE

The best thing about development design pattern is that once you know them, it turns out to be easy to map a design idea with a well know set of code lines. I learnt design pattern when I was in college and since then they have been useful tools to help me to build more flexible and extensible software system.

However, this one-to-one mapping between a specific model of the reality and a design pattern led me to think that perhaps I was missing the big picture. Also, implementing a design idea plugging in a well defined piece of code thinking to be applying a design pattern, has spawned a doubt in my mind: should design patterns really be a built-in language feature ?

I discovered in the end, that my understanding of patterns, and the way I was using them, frequently led me to over-engineer my work.

It took me a full immersion in the renovated world of agile methodologies to understand that the software I was building was not lean anymore.

Two principles in particular have changed my way of thinking about software: KISS (Keep It Simple, Stupid) and YAGNI (You aren’t gonna need it).

Only now I understand that “design to pattern” and “refactor to patterns” are two very different things. You should not feel obligated to use design pattern if you do not gain any meaningful benefit from them. Neither you should over engineer your product in behalf of creating an extensibility you may never going to need.

“One comment I saw in a news group just after patterns started to become more popular was someone claiming that in a particular program they tried to use all 23 GoF patterns. They said they had failed, because they were only able to use 20. They hoped the client would call them again to come back again so maybe they could squeeze in the other 3.

Trying to use all the patterns is a bad thing, because you will end up with synthetic designs—speculative designs that have flexibility that no one needs. These days software is too complex. We can’t afford to speculate what else it should do. We need to really focus on what it needs.”

– Erich Gamma 1

Bill Venners - How to Use Design Patterns1

DESIGN PATTERN PAPERT LUCA ALIBERTI !3

Page 4: AntiPatterns

DESIGN PATTERN PAPERT LUCA ALIBERTI !4

Page 5: AntiPatterns

Antipatterns Term Paper To engineer is human.

Introduction

The scientific literature is full of papers regarding design patterns. The same can’t be said about anti-patterns. Whereas patterns describe a recurring problem and its solution, antipatterns describe solutions that have more negative consequences than positive benefits. In effect, they describe dysfunctional approaches to problem solving, followed by the changes that should be made to overcome this dysfunction. The idea of antipatterns emerged soon after that of patterns, but it is unclear who first coined the term. In 1996, Michael Akroyd presented a paper at the Object World West Conference that documented harmful software constructs , 2

and Andrew Koenig published a short article in the Journal of Object-Oriented Programming using the term in 1995 . 3

Credit for promoting the term, however, must go to the authors of the antipatterns book by Brown et al . They expanded the scope of antipatterns to 4

include software project management as well, arriving at a three-pronged taxonomy of antipatterns: architectural, design, and management (Table 1.1). They define pattern and anti-pattern to be related: Design patterns can often evolve into AntiPatterns. A popular pattern, such as procedural programming, can be the popular paradigm of one era, and fall out of favour in the next as its consequences are better understood. The difference between patterns solutions and AntiPatterns solutions is that of context: An AntiPattern is a pattern in an inappropriate context. When a pattern becomes an AntiPattern, it is useful to have an approach for evolving the solution into a better one. This process of change, migration, or evolution is called refactoring. In refactoring, we change one solution to another

M. Akroyd, AntiPatterns: Vaccinations against Object Misuse, Proc. Object World West, 1996. 2

A. Koenig, Patterns and Antipatterns, Journal of Object-Oriented Programming, March–April, 1995. 3

! W.J.Brown, AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis. John Wiley & Sons, ltd4

DESIGN PATTERN PAPERT LUCA ALIBERTI !5

Page 6: AntiPatterns

solution with an improved structure, a structure that provides increased benefits. Also, over-engineering a solution for a simple and straight forward problem makes the process more complex and difficult to understands for the others. The use of a design pattern becomes an antipattern when it causes more problems than it solves. A McKinsey study in conjunction with the University of Oxford of 5,400 large 5

scale IT projects (projects with initial budgets greater than $15M) finds that the well known problems with IT Project Management are persisting. Among the key findings quoted from the report:

Delivering large-scale IT projects on time, on budget, and on value - October 2012 5

DESIGN PATTERN PAPERT LUCA ALIBERTI !6

Page 7: AntiPatterns

• 17 percent of large IT projects go so badly that they can threaten the very existence of the company;

• On average, large IT projects run 45 percent over budget and 7 percent over time, while delivering 56 percent less value than predicted.

A KPMG survey of Project Management practices in New Zealand finds some 6

truly startling results:

• Survey shows an incredible 70% of organisations have suffered at least one project failure in the prior 12 months;

• 50% of respondents also indicated that their project failed to consistently achieve what they set out to achieve!

There is a number of reason why the failure rates for IT project is so elevated. Some of them are clearly synthesised by June Verner : 7

• organisational structure, • unrealistic or unarticulated goals, • software that fails to meet the real business needs, • badly defined system requirements, user requirements and requirements specification, • the project management process, poor project management, • software development methodologies, sloppy development practices, • scheduling and project budget, • inaccurate estimates of needed resources, • poor reporting of the project status, • inability to handle project complexity, • unmanaged risks, • poor communication among customers,developers and users, • use of immature technology, • stakeholder politics, • commercial pressures, • customer satisfaction, • product quality,

NZ Project management survey 20106

What factors lead to software project failure? - June Verner7

DESIGN PATTERN PAPERT LUCA ALIBERTI !7

Page 8: AntiPatterns

• leadership, upper management support, • personality conflicts, • business processes and resources, • poor, or no tracking tools.

Many of the above are included in the Anti-pattern catalogue. The conclusion is that antipatterns are present in almost any organisation and software project ! They can be categorised into three groups, according to the following three View- points : 8

Development AntiPatterns mainly concern the software developer. They describe ”situations encountered by the programmer when solving programming problems”. Architectural AntiPatterns are important for the software architect. They focus on ”common problems in system structure, their consequences, and solutions” . Management AntiPatterns are relevant for the software project manager. They de- pict ”common problems and solutions due to the software organisation” . The aim of this paper is to show some useless and harmful sample applications of development design pattern and try to clarify when implementing design pattern in your application is a good choice and when, instead, it leads to the creation of an antipattern resulting in performance deterioration.

W.J.Brown, AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis. John Wiley & Sons, ltd8

DESIGN PATTERN PAPERT LUCA ALIBERTI !8

Page 9: AntiPatterns

Hello World !

If the alternative to an idiot using a pattern is that the idiot will try to invent something, I'll take the pattern.

-RonJeffries

Those who have some background in programming can confidently tell that the first program they usually learn to implement is “Hello World”. The simple and unique goal of the just said program is to print the text "Hello, World!" to the standard output. Now the simplest version of this program, in an object oriented programming language like Java, would look something like this:

public class Main { public static void main(String[] args) { System.out.println("Hello World!"); }}

Let’s see how the same “Hello World” program can be implemented using so desired design patterns. We first introduce the Observer design pattern that is well known to require two interfaces, the Subject and the Observer:

public interface Subject { public void attach(Observer observer); public void detach(Observer observer); public void notifyObservers(); } public interface Observer { public void update(Subject subject); }

DESIGN PATTERN PAPERT LUCA ALIBERTI !9

Page 10: AntiPatterns

Than we define two concrete classes implementing these two interfaces, the HelloWorldSubject and the HelloWorldObserver:

public class HelloWorldSubject implements Subject { private ArrayList<Observer> observers; private String str; public HelloWorldSubject() { super(); observers = new ArrayList<Observer>(); } public void attach(Observer observer) { observers.add(observer); }

public void detach(Observer observer) { observers.remove(observer); }

public void notifyObservers() { Iterator<Observer> iter = observers.iterator(); while (iter.hasNext()) { Observer observer = iter.next(); observer.update(this); } }

public String getStr() { return str; }

public void setStr(String str) { this.str = str; notifyObservers(); } }

DESIGN PATTERN PAPERT LUCA ALIBERTI !10

Page 11: AntiPatterns

public class HelloWorldObserver implements Observer { public void update(Subject subject) { HelloWorldSubject sub = (HelloWorldSubject)subject; System.out.println(sub.getStr()); }}

The HelloWorldObserver class will observe every time the method setStr on the class HelloWorldSubject is called and it will print out that string:

public class Main { public static void main(String[] args) { Observer observer = new HelloWorldObserver(); HelloWorldSubject subject = new HelloWorldSubject(); subject.attach(observer); subject.setStr("Hello World"); }}

This Main class will only just print: “Hello World” like the very first, one line example we have seen !

Going down this route we can introduce also the Command design pattern in this example; First of all we need a typical Command interface:

public interface Command { void execute();}

And a class that implements this interface:

public class HelloWorldCommand implements Command { private HelloWorldSubject subject; public HelloWorldCommand(Subject subject) { super(); this.subject = (HelloWorldSubject) subject; } public void execute() { subject.setStr("hello world"); }}

DESIGN PATTERN PAPERT LUCA ALIBERTI !11

Page 12: AntiPatterns

And the client would change to use this new class and call the execute method:

public class Main { public static void main(String[] args) { Observer observer = new HelloWorldObserver(); Subject subject = new HelloWorldSubject(); subject.attach(observer); HelloWorldCommand helloWorldCommand = new HelloWorldCommand (subject); helloWorldCommand.execute(); }}

Here, we are still just achieving exactly the same goal: print “hello world” to the standard output.

Finally we can add an implementation of the Factory Pattern as well; First we define an AbstractFactory interface:

public interface AbstractFactory { public Subject createSubject(); public Observer createObserver(); public Command createCommand(Subject subject); }

And then, a Factory class that created Subject, Object and Command objects:

public class HelloWorldFactory implements AbstractFactory { public Subject createSubject() { return new HelloWorldSubject(); } public Observer createObserver() { return new HelloWorldObserver(); } public Command createCommand(Subject subject) { return new HelloWorldCommand(subject); }}

DESIGN PATTERN PAPERT LUCA ALIBERTI !12

Page 13: AntiPatterns

The client would use this new factory for creating the objects it needs:

public class Main { public static void main(String[] args) { HelloWorldFactory helloWorldFactory = new HelloWorldFactory(); Observer observer = helloWorldFactory.createObserver(); Subject subject = helloWorldFactory.createSubject(); subject.attach(observer); Command helloWorldCommand = helloWorldFactory.createCommand(subject); helloWorldCommand.execute(); }}

Still, we are only printing “hello world” to the console. I could continue down this path applying a bunch of other well known patterns but I think the point is already very clear; Our application went from 3 lines of code to about one hundred, adding several level of indirection, creating new objects in memory, and making multiple and possibly expensive method calls. Did we gain in readability ? Obviously not, the application is much more difficult to read now. Did we gain in performance ? Definitely not, more object means more memory usage and more methods call means more time. Did we gain in extensibility ? Well this is arguable. Formally we did, because we could now add a brand new family of helloWorld objects for example, but who is ever going to need something like that ? Design pattern should not be applied for the sake of it. When there is no improvement in terms of performance, design, maintainability or at least readability of the code there is no space for patterns. Whenever I refactor existing code applying design pattern I always keep in mind the following deontological principle:

“Leave the world better than it is now “

DESIGN PATTERN PAPERT LUCA ALIBERTI !13

Page 14: AntiPatterns

You Aren’t Going To Need It

Providing flexibility that isn’t needed now and possibly never will be (YouArentGonnaNeedIt)? Far better to

KeepItSimple and use design patterns only when there is a benefit to be had now.

– DavidPeterson

One very important concept to understand is that there is a big difference in creating the most flexible possible solution for you problem that implements all known design patterns, and create an application that does just what you need, to refactor it later on, only if eventually needed.

Flexibility gained by refactoring is different from attempting to build it in beforehand.

"You aren't gonna need it” (acronym: YAGNI) is a principle of extreme programming (XP) that states a programmer should not add functionality until deemed necessary. XP co-founder Ron Jeffries has written: "Always implement things when you actually need them, never when you just foresee that you need them.”

In other words, YAGNI is a principle behind the XP practice of "do the simplest thing that could possibly work" (DTSTTCPW).

In my personal experience, since most developers have been told that the “Gang of Four” is a kind of holy book, this principle became very difficult to be understood.

Designing your application thinking that one day you may need to extend it ?

No, thanks ! This is a very anti-agile and anti-lean way of thinking and it has been shown to slow down your product time-to-market. In fact, the temptation to write code that is not necessary at the moment, but might be in the future, has the following disadvantages:

DESIGN PATTERN PAPERT LUCA ALIBERTI !14

Page 15: AntiPatterns

• The time spent is taken from adding, testing or improving the necessary functionality.

• The new features must be debugged, documented, and supported.

• Any new feature imposes constraints on what can be done in the future, so an unnecessary feature may preclude needed features from being added in the future.

• Until the feature is actually needed, it is difficult to fully define what it should do and to test it. If the new feature is not properly defined and tested, it may not work correctly, even if it eventually is needed.

• The software becomes larger and more complicated.

• Unless there are specifications and some kind of revision control, the feature may not be known to programmers who could make use of it.

• Adding the new feature may suggest other new features. If these new features are implemented as well, this could result in a snowball effect towards feature creep.

Blind adoption of the GangOfFour design patterns book as the one and only true way for program design may not be a good choice.

There is one wonderful metaphor from Jason Arhat that associate design patterns with martial arts that I could not forget to mention:

“I see patterns as being analogous to martial arts forms. The forms serve to preserve and pass on fighting techniques, and to demonstrate how those techniques work together. Practicing the forms correctly leads to insight into the techniques and gets the body used to moving in ways that hopefully make the person a better fighter. However, when you find yourself in an actual fight to defend yourself from bodily harm, if you go into one of your forms you'll get your butt kicked.

Patterns should be used the same way. They serve to pass on the experience of other programmers. Study them, practice them, learn their essence, then forget them. When you program, do the simplest thing that works. With the experience and insight you gained by studying the patterns you should be better able to see the simplest solution.”

DESIGN PATTERN PAPERT LUCA ALIBERTI !15

Page 16: AntiPatterns

Over-Engineering Vs Perfomance

So far, I've never actually implemented the VisitorPattern. The more times I don't implement it, the

more valuable it is to me. -- Phil Groce

Over engineering (or over-engineering) is designing a product to be more robust or complicated than is necessary for its application, either to ensure sufficient factor of safety, sufficient functionality, or because of design errors.

Too much flexibility in a design can be harmful. It makes for too many ways to modify the code. The implementation may become slow and hard to understand. Indirection can clog compilers and CPU pipelines as well.

I used to work maintaining a large (in codebase) application that was designed by people who were completely wild about patterns and insisted on their use at every turn; This particular system made extensive use of abstraction layers and factories. So much in fact that individual layers consisted of multiple layers and all communication between just about anything had to go through special objects. No object could be created except through a factory (often a special factory just for that object). That's over-design. Design with no perspective on the implementation of the design.

Only finding the class where to apply changes to get something done, often took more time than the actual change. Documentation was obviously obsolete because It couldn’t keep up with the level of indirection and layering created. Due to the incredible number of object constructions and method calls needed to get the simplest thing done, even simple operations often took incredible amounts of time. As a result some things HAD to be hacked in out of the design in order to get a feature implemented. For avoiding building new business objects that had to go trough all those layers for making simple calls to the database, some sql statements were written directly in the service layer. Database was starting to work as an integration point

DESIGN PATTERN PAPERT LUCA ALIBERTI !16

Page 17: AntiPatterns

between different components, observing every single row change and notifying the interested object hierarchy.

We were afraid to touch some parts of the code because we couldn't figure out what they did and how they did it,why and what could be the impact on other components. That's calcification of code.

As a result of such, a code freeze was decided while employing 80 per cent of our work force building a brand new version of the same software. The remaining 20 per cent of the department had the incredible and inaugurated task to fix every new high priority bug on the old application. It would take up to two weeks to fix one of these. Such was the dissatisfaction of the “SWAT” team, that high level managers had to issue a formal document where they stated that employees would start moving between the two teams with a bi-weekly frequency.

Do not get me wrong, in general patterns are not the problem. The problem is when and why to apply them! The DesignPatternsBook argues in favour of determining the patterns at a paper design stage (i.e., BigDesignUpFront). This leads to many of the woes described above. If, however, you follow DoTheSimplestThingThatCouldPossiblyWork and only apply patterns when the design asks for them, patterns become a very powerful tool. Some times the simplest solution is to use a concept that has already been proven in other implementations.

JimLittle, co-author of The Art of Agile Development , clearly separates software 9

design down into three categories:

1. Hacked-together. Hard to follow, hard to change, eventually dies under its own weight.

2. OverDesigned. Hard to understand, hard to change, calcifies over time.

3. Simple and minimalistic. Easy to understand, easy to change, seems to get more flexible over time.

Now, it would be a very complicated task for me to define a specific measure of how over engineering a software product affects its performance. On the other hand, there is some evidences that cannot be neglected. Munich Gupta synthesise some of 10

these:

• Excessive Layering - Most of the underlying performance starts with the excessive layering antipattern. The application design has grown over the usage of controllers, commands and facades. In order to decouple each layer, the designers are adding facades at each of the tiers. Theses exactly the scenario I describes before. Now, for every request at the web tier, the request call goes through

Co-author of The Art of Agile Development (O'Reilly, 2007)9

Munish Gupta - Application performance and antipatterns.10

DESIGN PATTERN PAPERT LUCA ALIBERTI !17

Page 18: AntiPatterns

multiple layers just to fetch the results. The number of objects that gets created and destroyed when making these calls add to the memory overhead. This further limits the amount of requests that can be handled by each server node. I’ve run a quick and simple experiment using a software called AppDynamics. With this software you can take a snapshot of any business transaction that is happening in your system and break down every single object method invocation. Developers may think that method invocation comes for free with today programming language. That is not true. I’ve noticed, with this experiment, that even the most simple method call can take 1 millisecond. If you have 10 layers in your system and you make 3 calls in each layer that would already take 30 milliseconds. And I am not taking in account much more expensive operation like database access or complex algorithm computation.

• Round Tripping- With the advent of ORM mappings, Session/DAO objects, the programmer starts making calls to beans for every data. This leading to excessive calls between the layers. Another side issue is the number of method calls each layer start having to support this model. Worse case is, when the beans are web service based. Client tier making multiple web service calls within a single user request have a direct impact on the application performance.

• Overstuffed Session. The application start with the promise of putting very minimal information in the session but over a period of time, the session object keeps on growing. Too much of data or wrong kind of data is stuffed into the session object. Large data objects will mean that the objects placed in the session will linger on till the session object is destroyed. This impacts the number of user’s that can be served by the application server node.

• Chatty Services – Another pattern observed is the way the service is implemented via multiple web service calls each of which is communicating a small piece of data. This results in explosion of web services and which leads to degradation of performance and unmaintainable code.

These are only some examples that shown how anti-pattern can impact performance.

DESIGN PATTERN PAPERT LUCA ALIBERTI !18

Page 19: AntiPatterns

The Simpleton Pattern

UNIX is very simple, but it takes a genius to understand the simplicity.

-- Dennis Ritchie

In the previous paragraph I have explained that over-engineering can affect the performance of a software system in a very bad way.

KISS is an acronym for "Keep it simple, stupid" as a design principle noted by the U.S. Navy in 1960. The KISS principle states that most systems work best if they are kept simple rather than made complicated; therefore simplicity should be a key goal in design and unnecessary complexity should be avoided.

If you have a choice between a simple solution and a complex one, then opting for complexity would be stupid.

After learning that there were two or three different ways to do a calculation, I'd immediately race toward implementing the Strategy pattern, when, in fact, a simple conditional expression would have been a perfectly sufficient solution.

A simple application is not only easy to create but it is also easy to read, understand and maintain. Many times I get to read extremely complex code using the latest library available, reflection and several design patterns all together. It takes me one day for understanding code that could have been written in one single unit tested class instead of implementing several architectural layers, indirections and design patterns.

Simplicity is also in very basic code style rules. Every method should be at max 7-10 lines long. Every class should be well-encapsulated and have a single responsibility.

Keep it simple every time you can and refactor only if you need is the principle I adopt !

DESIGN PATTERN PAPERT LUCA ALIBERTI !19

Page 20: AntiPatterns

Conclusion

Use of patterns where they fit has no effect on complexity at all. A lot of the patterns are about extensibility and reusability. When you really need extensibility, then patterns provide you with a way to achieve it ! When you need reusability, you should be able to customise behaviour without having to touch existing code. However when you don't need it, you should keep your design simple and not add unnecessary levels of indirection. When people starts learning design patterns, they try to use patterns everywhere and anyway; It does not matter whether a pattern is required or not. They think that the more patterns are used, the better is the design. The outcome is a code with unnecessary complexity. A pattern is a tool. Any fool who can misuse a tool will do so. Now in fact even the much maligned goto is not harmful if its only used when it should be that is never. So now what do we do hide all the tools? The answer is no. We should only perpetuate and spread the concept that pattern should be use only and only if they are needed in the perspective of building better but simpler application . 11

MarcGrundfest - Pattern are considered harmful11

DESIGN PATTERN PAPERT LUCA ALIBERTI !20

Page 21: AntiPatterns

DESIGN PATTERN PAPERT LUCA ALIBERTI !21