Sie sind auf Seite 1von 18

Software Engg Assignment

)AntiPatterns(

: Submitted by
Indranil Nandy
MTech, 2007
Roll no. : 06CS6010
Definition of AntiPatterns:
AntiPatterns are related to Design Patterns in that Design Patterns provide
solutions to recurring problems, while AntiPatterns are ”a literary form that describes
a commonly occurring solution to a problem that generates decidedly negative
consequences”
Design Patterns consist of a problem and a solution, while AntiPatterns consist
of two solutions – one which is problematic and one which is more beneficial, the
refactored solution.
AntiPatterns are useful in several ways:
• They provide a common vocabulary for known dysfunctional
software designs and solutions, as every AntiPattern has a short and
descriptive name like The Blob, Poltergeist or Golden Hammer.
• They help detecting problems in the code, the architecture and the
management of software projects.
• They describe both, preventive measures as well as refactored
solutions, which can save software projects in trouble.

AntiPatterns Reference Model:


AntiPatterns are distinguished from intuitive forms of software development
knowledge in that they are documented using a rigorous and consistent Reference
Model. A standardized template ensures that all aspects of an AntiPattern are properly
documented, i.e., that nothing is forgotten. Furthermore, such templates help the
reader to learn new AntiPatterns very quickly, as they all have the same structure.
Brown et. al. use the Mini-AntiPattern Template for very small and easily
understandable AntiPatterns. It consists of the AntiPattern Name, the AntiPattern
Problem, and the Refactored Solution. For more complex AntiPatterns the Full
AntiPattern Template is used, which contains additional items such as background,
general form, symptoms and consequences, typical causes, and known exceptions.

ViewPoints:

AntiPatterns are categorized into three groups, according to the following


three Viewpoints:

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
depict ”common problems and solutions due to the software organization”.
Root Causes:

Root Causes, also referred to as the ”seven deadly sins”, are software
development mistakes which lead to negative consequences like cost overruns or the
cancellation of projects. The following Root Causes can be identified:

Haste leads to compromises in software quality. Due to tight schedules and deadlines,
important activities such as unit testing and documenting source code are often
neglected in favor of ”getting things done”.

Apathy is even worse, in that it’s understood by the developers that something needs
to be improved, but they are unwilling to actually do it. This attitude of not caring
about solving known problems can have very negative consequences for the software
project.

Narrow-Mindedness is the basic resistance of developers against proven and


working solutions. For example, they might refuse to learn and use a unit-testing
framework, which would help to reduce the number of bugs in the code.

Sloth is characterized by decisions which always favor the most simple ”solution” or
answer to a problem. The consequence is improper and obscure software design,
which ultimately forces the developers to perform system discovery (trying to find out
how the software works) instead of writing or improving code.

Avarice or greed can produce wrong design decisions. Especially architectural


avarice can result in very complex and thus hard to maintain software. Proper
abstractions are missing due to modelling excessive details of the system.

Ignorance is the absence of motivation to understand things. This ”intellectual sloth”


usually leads to long-term problems such as massive implementation dependencies,
which make extending the software very hard.

Pride is also known as the ”not-invented-here syndrome”. Failure to reuse existing


software packages, components or libraries consumes more time and money for
(unnecessary) designing, implementing, debugging, testing and documenting the new
software components.

Software Design-Level Model:


The Software Design-Level Model partitions the problems and challenges of software
development according to their scale. This ensures a separation of concerns. The following
levels are used for categorizing AntiPatterns:

Object Level This is the finest-grained software-design level. At this level, the software
developer is concerned with classes, objects and their respective signatures and
implementations.

Micro-Architecture Level This level assembles a set of objects and combines them (often
using Design Patterns), creating a low-scale software architecture.
Framework Level This level is mainly concerned with macro-level architectures, which
usually involve multiple micro-architectures. The goal is to create reusable, flexible and
understandable software frameworks.

Application Level This level is concerned with the issues involving application design. The
main goal is to implement a set of functional and non-functional requirements in order to
deliver a complete application. This level often leverages one or more frameworks.

System Level This level comprises multiple applications which need to properly interact with
each other. Important issues here are life-cycle management, system evolution and especially
application compatibility issues.

Enterprise Level This is the largest architectural scale within a company and usually
involves multiple heterogeneous systems, each consisting of a set of interacting systems.

Global Level This is the largest software-design level across enterprise borders. It includes
multiple formal and de facto standards which are used by several companies, and are often
designed by consortiums or standardization bodies.

Recognized Software Development AntiPatterns:


Software Development AntiPatterns can be classified into seven
broad categories as follows :
1. Project management anti-patterns
2. General design anti-patterns
3. Object-oriented design anti-patterns
4. Programming anti-patterns
5. Methodological anti-patterns
6. Configuration management anti-patterns
7. Organizational anti-patterns

First, two well known antipatterns will be explained. After that,


various antipatterns under each of the aforesaid categories will be
discussed briefly.

Spaghetti Code
AntiPattern Name: Spaghetti Code
Most Applicable Scale: Application
Refactored Solution Name: Software Refactoring, Code Cleanup
Refactored Solution Type: Software
Root Causes: Ignorance, Sloth
Unbalanced Forces: Management of Complexity, Change

Description:
The Spaghetti Code AntiPattern is probably the most well-known and the most
widespread AntiPattern. In the early days of computing and programming, when the
design of software systems was not very well understood and often done in an ad-hoc
style, Spaghetti Code was pervasive. But even today, in the age of object-oriented
programming (OOP) and high-level languages, the pattern still persists.

Symptoms of this AntiPattern include, but are not limited to, a) confusing,
inconsistent or barely existing code structure, b) excessive usage of global variables,
and c) the usage of GOTOs. The result is unmaintainable code, which is very hard to
read, understand and reuse.

Programming Languages have a strong impact on the readability and


maintainability of the code. Some programming languages, such as BASIC or Perl,
tend to foster the development of Spaghetti Code by allowing, encouraging or even
requiring the usage of problematic programming constructs. Other languages, such as
Ruby, suggest a much better programming style which does not employ such
unstructured programming constructs.Many ”good” examples for unreadable and
unmaintainable code stem from The International Obfuscated C Code Contest, a
programming contest where the submission with the most obfuscated and unreadable
C code wins.

Lack of Understanding of OOP concepts is another typical cause of the Spaghetti


Code AntiPattern. Many unexperienced OOP developers, especially those who have a
strong background in procedural languages such as C, tend to emulate procedural
algorithms and data-structures. This often results in the abuse or neglection of central
OOP features such as inheritance, encapsulation and polymorphism. The following
code fragment is an impressive example:

if ( obj instance of MMSNode) {


MMSNode node = (MMSNode) obj;
attribs = node.getAttribs( ) ;
}
else if ( obj instance of MMSPhysComp) {
MMSPhysComp physComp = (MMSPhysComp) obj ;
attribs = physComp.getAttribs( ) ;
}
else if ( obj instance of MMSLogComp) {
MMSLogComp logComp = (MMSLogComp) obj ;
attribs = logComp.getAttribs( ) ;
}
else if ( obj instance of MMSPhysLink ) {
MMSPhysLink physLink = (MMSPhysLink ) obj ;
attribs = physLink.getAttribs( ) ;
}

The programmer who wrote this Java code, did not use polymorphism
properly. The classes MMSPhysComp, MMSLogComp, MMSPhysLink most
probably are (or should be) subclasses of the common parent-class MMSNode. A
more concise and more readable solution would be something like:
attribs = ( (MMSNode) obj ) . getAttribs( ) ;
The built-in polymorphism of the programming language will automatically
and transparently choose and call the respectivemethod. The code not only becomes
shorter and probably more efficient, but more importantly it becomes a lot more
readable.
Refactored Solution:

In general, the refactored solution for the Spaghetti Code AntiPattern is code
cleanup or refactoring. This comprises a multitude of activities such as renaming
classes, methods, and variables for more consistency, removing dead code, enforcing
coding standards, refactoring the code in order to be reusable.
In short, regular code cleanups should be performed during development. To
retrospectively clean up and ship a software system after months or even years of
unstructured development is definitely doomed to fail.

Cut-and-Paste Programming
AntiPattern Name: Cut-and-Paste Programming
Also Known As: Clipboard Coding, Software Cloning, Software Propagation
Most Applicable Scale: Application
Refactored Solution Name: Black Box Reuse
Refactored Solution Type: Software
Root Causes: Sloth
Unbalanced Forces: Management of Resources, Technology Transfer

Description:
Cut-and-Paste Programming is a form of code reuse and commonly practiced
in software development.However, it is also a major source of bugs and
maintainability problems. In Cut-and-Paste Programming, several similar code
fragments co-exist in the same program. These fragments usually come from code
which is copied multiple times by the developers. Each instance of such a cloned code
is then adapted to the local environment in which it is used. This bears multiple
problems:
• First, the same bugs often reoccur although they have been fixed locally multiple
times. This is the case because each instance of the copied code fragment must be
found and fixed individually. In addition to that, each code fragment needs a slightly
different bugfix, which makes software maintenance a very time consuming and error-
prone task.
• The code bloat produced by Cut-and-Paste Programming has no benefit in terms of
additional features or better code quality. On the contrary, the code becomes a lot
harder to read and understand.
• Code inspections, reviews and walkthroughs become unnecessarily long and
complicated, simply because more code needs to be analyzed. Consider this small
example from Eclipse 3.0 (an open source software development platform):
From the class SimpleLookupTable:

publ i c boolean containsKey ( Object key ) {


int length = keyTable.length;
int index = ( key.hashCode ( ) & 0x7FFFFFFF ) % length;
Object currentKey;
whi l e ( ( currentKey = keyTable [ index ] ) != null ) {
if ( currentKey.equals ( key ) ) return true;
if (++ index == length ) index = 0 ;
}
return false ;
}

From the class SimpleTest:

publ i c boolean includes ( Object object ) {


int length = values.length;
int index = ( object.hashCode ( ) & 0x7FFFFFFF ) % length;
Object current;
while ( ( current = variables [ index ] ) != null ) {
if ( current.equals ( object ) ) return true ;
if (++ index == length ) index = 0 ;
}
return false ;
}

Both methods are identical barring the renaming of variables, thus they are good
candidates for refactoring.

Refactored Solution:

Although there are situations where Cut-and-Paste Programming is a


comfortable or even necessary technique, it should be avoided, or at least minimized,
in its application.
Clone Detection has been proposed in the literature as a way to find duplicated or
similar code in software projects. Multiple detection techniques have been described
and also implemented in prototypical tools.
Line-based detection is the simplest technique. Basically, lines of text (e.g. source
code) are compared to each other to find duplicated text passages. Sometimes simple
transformations are performed, such as removing whitespace and comments.
Token-based techniques perform a lexical analysis on the source code. The resulting
stream of tokens is then analyzed for duplicated token sequences.
AST-based detection generates an abstract syntax tree (AST) fromthe source code.
Then, code clones are detected by finding similar or identical subtrees in this AST.
PDG-based analysis goes even further. Prior to the detection step, a program
dependence graph (PDG) is created, which contains information about the control
flow and data flow of the program.
Metric-based approaches, finally, leverage various code metrics to associate a small
fingerprint with each code fragment, similar to what a hashing function does. The idea
is, that code fragments with identical or similar fingerprints (and thus metrics) should
be (near-)identical.

Using such tools, developers can easily find duplicated code fragments in their
projects. Manual search for code clones would be impractical for smaller projects and
next to impossible for large software projects with several million lines of code.
Refactoring is the next logical step, after code clones have been identified.
Fowler defines Refactoring as ”the process of changing a software system in such a
way that it does not alter the external behavior of the code yet improves its internal
structure.”. For the purposes of clone removal, the duplicated code is factored out to a
common function, method, or class which is then used instead of the duplicated code.
This ensures that bugs must only be fixed in one place and only once.
Now, we will discuss briefly about some other antipatterns :

 Project management anti-patterns

Smoke and Mirrors :

Smoke and mirrors is a metaphor for a deceptive, fraudulent or insubstantial


explanation or description. The source of the name is based on magicians' illusions,
where magicians use smoke and mirrors to accomplish illusions such as making
objects disappear, when they really don't disappear at all. The expression may have a
connotation of virtuosity or cleverness in carrying out such a deception.

In the field of computer programming, it is used to describe a program or


functionality that doesn't exist yet, but appears as though it does. This is often done to
demonstrate what a resulting project will function/look like after the code is complete.
Smoke and mirrors is not recommended when displaying any program, as it can lead
to expectations for a feature that turns out to be impossible or extremely difficult to
implement.

More generally, "smoke and mirrors" may refer to any sort of presentation by which
the audience is intended to be deceived, such as an attempt to fool a prospective client
into thinking that one has capabilities necessary to deliver a product in question.

Software bloat:

Software bloat (or resource hog) is a term used in both a neutral and disparaging
sense, to describe the tendency of newer computer programs to be larger, or to use
larger amounts of system resources (mass storage space, processing power or
memory) than older versions of the same programs, without concomitant benefits
being provided to end users.

Bloat is ascribed to various causes including: the tendency to replace efficiency-


focused applications with less efficient enhanced versions, inefficiencies or
unnecessary modules in program design and operation, and the incorporation of
extended features which will be extraneous or low value for most users but slow down
the program overall even if unused.

The latter is often blamed either on the prioritization of marketing and "headline
feature-set" over quality and focus, or the need to be perceived as adding new
functionality in the software market, which for many products relies upon the
existence of regular enhanced versions to be sold within the existing user base.

It is also used more generally to describe programs which appear to be using more
system resources than necessary. Software exhibiting these tendencies is referred to as
bloatware, resource hog or, less commonly, fatware.
 General design anti-patterns
Abstraction Inversion:

In computer programming, abstraction inversion is an anti-pattern arising when the


interface to a construct does not expose the functions needed by its users, although
they are used within the construct. The result is that the users re-implement the
required functions in terms of the interface, which in its turn uses the internal
implementation of the same functions.

The term "abstraction inversion" is sometimes misunderstood as referring to complex


(or concrete) constructs with simple (or abstract) interfaces, which are normal and
desirable. It is also sometimes misused as an insult against a certain architecture or
design.

Possible ill-effects are:

• The user of such a re-implemented function may seriously underestimate its


running-costs.
• The user of the construct is forced to obscure his implementation with
complex mechanical details.
• Many users attempt to solve the same problem, increasing the risk of error.

Abstraction inversion in practice

Ways to avoid this anti-pattern include:

For designers of lower-level software:

• If your system offers formally equivalent functions, choose carefully


which to implement in terms of the other.
• Do not force unnecessarily weak constructs on your users.

For implementers of higher-level software:

• Choose your infrastructure carefully.

Examples

Alleged examples from professional programming circles include:

• In Ada, choice of the rendezvous construct as a synchronisation primitive


forced programmers to implement simpler constructs such as semaphores on
the more complex basis.
• In Applesoft BASIC, integer arithmetic was implemented on top of floating-
point arithmetic, and there were no bitwise operators and no support for
blitting of raster graphics (even though the language supported vector graphics
on the Apple II's raster hardware). This caused games and other programs
written in BASIC to run more slowly.
Big Ball of Mud:

The term was popularised in Brian Foote and Joseph Yoder's 1999 paper of the same
name, which defines the term thus:

A Big Ball of Mud is a haphazardly structured, sprawling, sloppy, duct-tape-


and-baling-wire, spaghetti-code jungle. These systems show unmistakable
signs of unregulated growth, and repeated, expedient repair. Information is
shared promiscuously among distant elements of the system, often to the point
where nearly all the important information becomes global or duplicated. The
overall structure of the system may never have been well defined. If it was, it
may have eroded beyond recognition. Programmers with a shred of
architectural sensibility shun these quagmires. Only those who are
unconcerned about architecture, and, perhaps, are comfortable with the
inertia of the day-to-day chore of patching the holes in these failing dikes, are
content to work on such systems.

Big Ball of Mud systems were usually developed over a period of time with different
individuals working on various pieces and parts. Expediency plays a major role.
Systems developed by people with no formal computer architecture or programming
training often fall into this pattern.

Foote and Yoder do not universally condemn Big Ball of Mud programming, pointing
out that this pattern is most prevalent because it works — at least for the moment.
However, programs of this pattern become maintenance nightmares.

Programmers in control of a big ball of mud project are strongly encouraged to study
it and to understand what it accomplishes and use this as a loose basis for a formal set
of requirements for the new well architected system that would be developed to
replace the former. Technology shifts (client-server to web-based, file-based to
database-based, etc.) can provide good reasons to start over from scratch.

Race Hazard:

A race condition or race hazard is a flaw in a system or process whereby the


output of the process is unexpectedly and critically dependent on the sequence or
timing of other events. The term originates with the idea of two signals racing each
other to influence the output first.

Race conditions can occur in poorly-designed electronics systems, especially logic


circuits, but they can and often do also arise in computer software.

Electronics

A typical example of a race condition may occur in a system of logic gates, where
inputs vary. If a particular output depends on the state of the inputs, it may only be
defined for steady-state signals. As the inputs change state, a finite delay will occur
before the output changes, due to the physical nature of the electronic system. For a
brief period, the output may change to an unwanted state before settling back to the
designed state. Certain systems can tolerate such glitches, but if for example this
output signal functions as a clock for further systems that contain memory, the system
can rapidly depart from its designed behavior (in effect, the temporary glitch becomes
permanent).

For example, consider a two input AND gate fed with a logic signal X on input A and
its negation, NOT X, on input B. In theory, the output (X AND NOT X) should never
be high. However, if changes in the value of X take longer to propagate to input B
than to input A then when X changes from false to true, a brief period will ensue
during which both inputs are true, and so the gate's output will also be true.

Proper design techniques (e.g. Karnaugh maps—note, the Karnaugh map article
includes a concrete example of a race condition and how to eliminate it) encourage
designers to recognise and eliminate race conditions before they cause problems.

As well as these problems, logic gates can enter metastable states, which create
further problems for circuit designers.

Types
Static race conditions
These are caused when a signal and its complement are combined together.
Dynamic race conditions
These result in multiple transitions when only one is intended. They are due to
interaction between gates (Dynamic race conditions can be eliminated by
using not more than two levels of gating).
Essential race conditions
These are caused when an input has two transitions in less than the total
feedback propagation time. Sometimes they are cured using inductive delay-
line elements to effectively increase the time duration of an input signal.

Computing

Race conditions may arise in software, especially when communicating between


separate processes or threads of execution.

Here is a simple example:

Let us assume that two threads T1 and T2 each want to increment the value of a
global integer by one. Ideally, the following sequence of operations would take place:

1. Integer i = 0;
2. T1 reads the value of i from memory into a register : 0
3. T1 increments the value of i in the register: (register contents) + 1 = 1
4. T1 stores the value of the register in memory : 1
5. T2 reads the value of i from memory into a register : 1
6. T2 increments the value of i in the register: (register contents) + 1 = 2
7. T2 stores the value of the register in memory : 2
8. Integer i = 2
In the case shown above, the final value of i is 2, as expected. However, if the two
threads run simultaneously without locking or synchronization, the outcome of the
operation could be wrong. The alternative sequence of operations below demonstrates
this scenario:

1. Integer i = 0;
2. T1 reads the value of i from memory into a register : 0
3. T2 reads the value of i from memory into a register : 0
4. T1 increments the value of i in the register: (register contents) + 1 = 1
5. T2 increments the value of i in the register: (register contents) + 1 = 1
6. T1 stores the value of the register in memory : 1
7. T2 stores the value of the register in memory : 1
8. Integer i = 1

The final value of i is 1 instead of the expected result of 2. This occurs because the
increment operations of the second case are non-atomic. Atomic operations are those
that cannot be interrupted while accessing some resource, such as a memory location.
In the first case, T1 was not interrupted while accessing the variable i, so its operation
was atomic.

For another example, consider the following two tasks, in pseudocode:

global integer A = 0;

// increments the value of A and print "RX"


// activated whenever an interrupt is received from the serial
controller
task Received()
{
A = A + 1;
print "RX";
}

// prints out only the even numbers


// is activated every second
task Timeout()
{
if (A is divisible by 2)
{
print A;
}
}

Output would look something like:

0
0
0
RX
RX
2
RX
RX
4
4
Now consider this chain of events, which might occur next:

1. timeout occurs, activating task Timeout


2. task Timeout evaluates A and finds it is divisible by 2, so elects to execute the
"print A" next.
3. data is received on the serial port, causing an interrupt and a switch to task
Received
4. task Received runs to completion, incrementing A and printing "RX"
5. control returns to task Timeout
6. task timeout executes print A, using the current value of A, which is 5.

Mutexes are used to address this problem in concurrent programming.

Real life examples

File systems

In file systems, two or more programs may "collide" in their attempts to modify or
access a file, which could result in data corruption. File locking provides a
commonly-used solution. A more cumbersome remedy involves reorganizing the
system in such a way that one unique process (running a daemon or the like) has
exclusive access to the file, and all other processes that need to access the data in that
file do so only via interprocess communication with that one process (which of course
requires synchronization at the process level).

A different form of race hazard exists in file systems where unrelated programs may
affect each other by suddenly using up available resources such as disk space (or
memory, or processor cycles). Software not carefully designed to anticipate and
handle this rare situation may then become quite fragile and unpredictable. Such a risk
may be overlooked for a long time in a system that seems very reliable. But
eventually enough data may accumulate or enough other software may be added to
critically destabilize many parts of a system. Probably the best known example of this
occurred with the near-loss of the Mars Rover "Spirit" not long after landing, but this
is a commonly overlooked hazard in many computer systems. A solution is for
software to request and reserve all the resources it will need before beginning a task;
if this request fails then the task is postponed, avoiding the many points where failure
could have occurred. (Alternately, each of those points can be equipped with error
handling, or the success of the entire task can be verified before proceeding
afterwards.) A more common but incorrect approach is to simply verify that enough
disk space (for example) is available before starting a task; this is not adequate
because in complex systems the actions of other running programs can be
unpredictable.

Networking

In networking, consider a distributed chat network like IRC, where a user acquires
channel-operator privileges in any channel he starts. If two users on different servers,
on different ends of the same network, try to start the same-named channel at the
same time, each user's respective server will grant channel-operator privileges to each
user, since neither server will yet have received the other server's signal that it has
allocated that channel.

In this case of a race condition, the concept of the "shared resource" covers the state
of the network (what channels exist, as well as what users started them and therefore
have what privileges), which each server can freely change as long as it signals the
other servers on the network about the changes so that they can update their
conception of the state of the network. However, the latency across the network
makes possible the kind of race condition described. In this case, heading off race
conditions by imposing a form of control over access to the shared resource—say,
appointing one server to control who holds what privileges—would mean turning the
distributed network into a centralized one (at least for that one part of the network
operation). Where users find such a solution unacceptable, a pragmatic solution can
have the system 1) recognize when a race condition has occurred; and 2) repair the ill
effects.

Magic PushButton:

The magic pushbutton anti-pattern is very common in graphical


programming environments. In this scenario, the programmer draws the user interface
first and then writes the business logic in the automatically created methods.

Problems with this anti-pattern are:

• The code behind the Pushbuttons grows unmanageably


• Changing the user interface (or adding an alternate interface) is difficult
• Testing the code is difficult

Bad Example )Borland Delphi(

procedure TForm1.Button1Click(Sender: TObject);


var
reg:TRegistry;
begin
reg:=TRegistry.Create;
try
reg.RootKey:=HKey_Current_User;
if reg.OpenKey('\Software\MyCompany',true) then
begin
reg.WriteString('Filename',edit1.text);
end;
finally
reg.Free;
end;
end;

Good Example )Borland Delphi(

A better way to do this is to refactor the business logic (in this example storing the
filename to the registry) into a separate class.

type
TPreferences = class
private
FFilename: string;
procedure SetFilename(const Value: string);
public
property Filename:string read FFilename write SetFilename;
procedure Load;
procedure Save;
end;

and call this class Save method from the Click handler:

procedure TForm1.Button1Click(Sender: TObject);


begin
Preferences.Save;
end;
procedure TForm1.Edit1Change(Sender: TObject);
begin
Preferences.Filename:=edit1.text;
end;

 Object-oriented design anti-patterns

God Object:

In object-oriented programming, a God object is an object that knows too


much or does too much. The God object is an example of an anti-pattern.

The basic idea behind structured programming is that a big problem is broken down
into many smaller problems (divide and conquer) and solutions are created for each of
them. If you are able to solve all of the small problems, you have solved the big
problem as a whole. Therefore there is only one object about which an object needs to
know everything: itself. And there is only one problem an object needs to solve: its
own.

God object-based code does not follow this approach. Instead, much of a program's
overall functionality is coded into a single object. Because this object holds so much
data and has so many methods, its role in the program becomes God-like (all-
encompassing).

Instead of objects communicating amongst themselves directly, the other objects rely
on the God object. Because the God object is referenced by so much of the other code,
maintenance becomes more difficult than it otherwise would.

A God object is the object-oriented analogue of failing to use subroutines in


procedural programming languages, or using far too many global variables to store
state information.

While bad programming practice, a God object is occasionally created in tight


environments such as microcontrollers where the slight performance increase is more
important than maintainability. As microcontrollers increase in power, this should
become less and less true.
Yo-yo problem:

In computer science, the yo-yo problem is an anti-pattern that occurs when a


programmer has to read and understand a program whose inheritance graph is so long
and complicated that the programmer has to keep flipping between many different
class definitions in order to follow the control flow of the program. It often happens in
object-oriented programming. The term comes from comparing the bouncing attention
of the programmer to the up-down movement of a toy yo-yo.

Most practices of object-oriented programming recommend keeping the inheritance


graph as shallow as possible, in part to avoid this problem. The use of composition
instead of inheritance is also strongly preferred, although this still requires that a
programmer keep multiple class definitions in mind at once.

More generally, the yo-yo problem can also refer to any situation where a person must
keep flipping between different sources of information in order to understand a
concept.

Object-oriented design techniques such as documenting layers of the inheritance


hierarchy can reduce the effect of this problem, as they collect in one place the
information that the programmer is required to understand.

 Programming anti-patterns

Blind Faith:

In computer programming blind faith (also known as Blind programming or


blind coding) means that a programmer develops a solution or fixes a computer bug
and deploys it without ever testing his creation. The programmer in this situation has
blind faith in his own abilities, but this often results in catastrophic failure.

Another form of blind faith is when a programmer calls a subroutine without checking
the result. E.g.: A programmer calls a subroutine to save user-data on the harddisk
without checking whether the operation was successful or not. In this case the
programmer has blind faith in the subroutine always performing what the programmer
intends to accomplish.

Blind faith is an example of an Anti-pattern. Another common name for Blind Faith is
"God oriented programming" or "Divine orientation".

Blind faith programming can also be used as a challenge to test programming skills.

Accumulate and Fire:

"Accumulate and fire" is a programming style in which the program sets a


number of global variables or objects, then invokes subroutines or methods which
operate on the globally set values.
This is considered problematic because:

• it is not evident when reading the program which globals serve as arguments,
• programmers can forget to set a value before invoking,
• the state can change in a moment between setting and invoking, particularly
when programming with threading,
• the global variables may be corrupted by being unintentionally used to pass
values to more than one subroutine.
• it makes the use of recursion much more awkward.

Some older programming languages make this style hard to avoid: this is the only way
of passing arguments to a paragraph in BASIC (using GOSUB) or COBOL.

Accumulate and fire is considered an example of an anti-pattern.

Ravioli Code:

Ravioli code is a type of program structure, characterized by a number of


small and (ideally) loosely-coupled software components. The term is in comparison
with spaghetti code, comparing program structure to pasta; with ravioli (small pasta
pouches containing cheese, meat, or vegetables) being analogous to objects (which
ideally are encapsulated modules consisting of both code and data).

Some consider ravioli code to be a good design methodology, especially when the
components used are modular, highly interchangeable, well-encapsulated, and
providing well-defined interfaces and behavior. Others consider ravioli code to be an
anti-pattern. In poorly-designed object-oriented systems, with deep inheritance
hierarchies and multiple layers of virtual functions overriding each other, it can
become very difficult to discern (without use of a debugger) exactly what the
behavior of the program is, as it is often unclear how virtual function calls are
resolved.

Conclusion:

In general, many software projects suffer from severe problems, be it cost


overruns, maintainability problems or even the cancellation of the whole project.
AntiPatterns provide a common terminology and reference model of recurring
problems in software design and implementation. They can help identify potential
problems in software projects. Furthermore, they propose refactored solutions, which
help to solve the problems.
Finally, by studying AntiPatterns, their symptoms, consequences and
solutions, one can prevent the occurrence of these problems altogether.

References:

http://www.hermann-uwe.de/files/antipatterns.pdf

http://www.eclipse.org
http://www.ioccc.org

Das könnte Ihnen auch gefallen