tags:

views:

1342

answers:

13

When designing a new system or getting your head around someone else's code, what are some tell tale signs that something has gone wrong in the design phase? Are there clues to look for on class diagrams and inheritance hierarchies or even in the code itself that just scream for a design overhaul, particularly early in a project?

+2  A: 

One thing I hate to see is a base class down-casting itself to a derived class. When you see this, you know you have problems.

Other examples might be:

  • Excessive use of switch statements
  • Derived classes that override everything
Brian Genisio
"a base class down-casting itself to a derived class" - as always there's an exception, which when you use CRTP to implement simulated dynamic binding in C++. The derived class is supplied to the base class as a template parameter, so you absolutely definitely know the cast is valid.
Steve Jessop
That is very different than what I am talking about. I am talking about when a Vehicle class knows that the Car class exists. In your case, the base class still has no knowledge of the derived class. Like you said, exceptions to rules. I wouldn't even call them rules... warning smells, perhaps?
Brian Genisio
Well, the base *class* does have specific knowledge of the derived class, it's just that the programmer only deals directly with the base *class template*, which doesn't. C++ is pure gold for pedants ;-)
Steve Jessop
+1  A: 

Here's a few:

  • Circular dependencies
  • You with property XYZ of a base class wasn't protected/private
  • You wish your language supported multiple inheritance
Lasse V. Karlsen
I have to disagree with the third one. Sometimes, MI can be a good thing if used properly.
Jason Baker
+28  A: 

The things that mostly stick out for me are "code smells".

Mostly I'm sensitive to things that go against "good practice".

Things like ...

... Methods that do things other than what you'd think from the name (eg: FileExists() that silently deletes zero byte files)

... A few extremely long methods (sign of an object wrapper around a procedure)

... Repeated use of switch/case statements on the same enumerated member (sign of sub-classes needing extraction)

... Lots of member variables that are used for processing, not to capture state (might indicate need to extract a method object)

... A class that has lots of responsibilities (violation of Single Repsonsibility principle)

... Long chains of member access (this.that is fine, this.that.theOther is fine, but my.very.long.chain.of.member.accesses.for.a.result is brittle)

... Poor naming of classes

... Use of too many design patterns in a small space

... Working too hard (rewriting functions already present in the framework, or elsewhere in the same project)

... Poor spelling (anywhere) and grammar (in comments), or comments that are simply misleading

Bevan
Nice list, +1. I'm however curious about the excessive design pattern density. Have you really encounter that ? I'd say it's more when patterns are mis-used and abused.
philippe
+1  A: 

In my view, all OOP code degenerates to procedural code over a sufficiently long time span.

Granted, if you read my most recent question, you might understand why I am a little jaded.

The key problem with OOP is that it doesn't make it obvious that your object construction graph should be independent of your call graph.

Once you fix that problem, OOP actually starts to make sense. The problem is that very few teams are aware of this design pattern.

Simon Johnson
A: 

Having all you objects inherit some base utility class just so you can call your utility methods without having to type so much code.

Kibbee
+9  A: 

Impossible to unit test properly.

Hates_
Bingo! See my comment. The worst part is that at least 90% of the code you'll ever see is impossible to unit test properly.
Simon Johnson
I believe this is the same thing as saying "high coupling".
Jay Bazuzi
+11  A: 

Check this out. Its awesome.

Bad Smells: http://c2.com/xp/CodeSmell.html

Its not specific to object oriented programming, but it covers a lot of bits. Cheers.

Adeel Ansari
You are welcome, Jason.
Adeel Ansari
Server seems to be down, do we have to call it "stackoverflown" effect (in addition to "slashdotted")?
dhiller
@Dhiller: Yes, it seems down. Anyways, found at another place.
Adeel Ansari
+12  A: 

I'd say the number one rule of poor OO design (and yes I've been guilty of it too many times!) is:

  • Classes that break the Single Responsibility Principle (SRP) and perform too many actions

Followed by:

  • Too much inheritance instead of composition, i.e. Classes that derive from a sub-type purely so they get functionality for free. Favour Composition over Inheritance.
Mitch Wheat
True. Would like to include a couple of more, Programming to an Interface, and Seperation of Concerns.
Adeel Ansari
+3  A: 

This question makes the assumption that object-oriented means good design. There are cases where another approach is much more appropriate.

Kozyarchuk
+1, although I would say "another approach" rather than "functional approach". There are very few design approaches which do not have at least a small class of problems for which they are the most appropriate choice.
Dave Sherohman
+2  A: 

One smell is objects having hard dependencies/references to other objects that aren't a part of their natural object hierarchy or domain related composition.

Example: Say you have a city simulation. If the a Person object has a NearestPostOffice property you are probably in trouble.

Daniel Auger
A: 

Find a programmer who is experienced with the code base. Ask them to explain how something works.

If they say "this function calls that function", their code is procedural.

If they say "this class interacts with that class", their code is OO.

Jay Bazuzi
What if both classes are singletons =)
FlySwat
I mean "if they describe their code as the relationships between classes, it's OO; as the relationships between methods, it's procedural; as the relationships between statements, it's spagetti"
Jay Bazuzi
+1  A: 

Within a long method, sections surrounded with #region / #endregion - in almost every case I've seen, that code could easily be extracted into a new method OR needed to be refactored in some way.

Overly-complicated inheritance trees, where the sub-classes do very different things and are only tangentially related to one another.

Violation of DRY - sub-classes that each override a base method in almost exactly the same way, with only a minor variation. An example: I recently worked on some code where the subclasses each overrode a base method and where the only difference was a type test ("x is ThisType" vs "x is ThatType"). I implemented a method in the base that took a generic type T, that it then used in the test. Each child could then call the base implementation, passing the type it wanted to test against. This trimmed about 30 lines of code from each of 8 different child classes.

GalacticCowboy
+5  A: 

Anti-patterns

Software design anti-patterns

  • Abstraction inversion : Not exposing implemented functionality required by users, so that they re-implement it using higher level functions
  • Ambiguous viewpoint: Presenting a model (usually OOAD) without specifying its viewpoint
  • Big ball of mud: A system with no recognizable structure
  • Blob: Generalization of God object from object-oriented design
  • Gas factory: An unnecessarily complex design
  • Input kludge: Failing to specify and implement handling of possibly invalid input
  • Interface bloat: Making an interface so powerful that it is extremely difficult to implement
  • Magic pushbutton: Coding implementation logic directly within interface code, without using abstraction.
  • Race hazard: Failing to see the consequence of different orders of events
  • Railroaded solution: A proposed solution that while poor, is the only one available due to poor foresight and inflexibility in other areas of the design
  • Re-coupling: Introducing unnecessary object dependency
  • Stovepipe system: A barely maintainable assemblage of ill-related components
  • Staralised schema: A database schema containing dual purpose tables for normalised and datamart use

Object-oriented design anti-patterns

  • Anemic Domain Model: The use of domain model without any business logic which is not OOP because each object should have both attributes and behaviors
  • BaseBean: Inheriting functionality from a utility class rather than delegating to it
  • Call super: Requiring subclasses to call a superclass's overridden method
  • Circle-ellipse problem: Subtyping variable-types on the basis of value-subtypes
  • Empty subclass failure: Creating a class that fails the "Empty Subclass Test" by behaving differently from a class derived from it without modifications
  • God object: Concentrating too many functions in a single part of the design (class)
  • Object cesspool: Reusing objects whose state does not conform to the (possibly implicit) contract for re-use
  • Object orgy: Failing to properly encapsulate objects permitting unrestricted access to their internals
  • Poltergeists: Objects whose sole purpose is to pass information to another object
  • Sequential coupling: A class that requires its methods to be called in a particular order
  • Singletonitis: The overuse of the singleton pattern
  • Yet Another Useless Layer: Adding unnecessary layers to a program, library or framework. This became popular after the first book on programming patterns.
  • Yo-yo problem: A structure (e.g., of inheritance) that is hard to understand due to excessive fragmentation
philippe