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. 2. 3. 4. 5. 6. 7.
Project management anti-patterns General design anti-patterns Object-oriented design anti-patterns Programming anti-patterns Methodological anti-patterns Configuration management anti-patterns 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 errorprone 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 efficiencyfocused 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 floatingpoint 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-tapeand-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 delayline 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. 2. 3. 4. 5. 6. 7. 8.
Integer i = 0; T1 reads the value of i from memory into a register : 0 T1 increments the value of i in the register: (register contents) + 1 = 1 T1 stores the value of the register in memory : 1 T2 reads the value of i from memory into a register : 1 T2 increments the value of i in the register: (register contents) + 1 = 2 T2 stores the value of the register in memory : 2 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. 2. 3. 4. 5. 6. 7. 8.
Integer i = 0; T1 reads the value of i from memory into a register : 0 T2 reads the value of i from memory into a register : 0 T1 increments the value of i in the register: (register contents) + 1 = 1 T2 increments the value of i in the register: (register contents) + 1 = 1 T1 stores the value of the register in memory : 1 T2 stores the value of the register in memory : 1 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 (allencompassing). 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