DEV Community

CodingBlocks

Episode 67 – Object Oriented Mistakes

Allen brings the dad jokes, Michael unplugs, and Joe gets a second wind as we discuss the anti-patterns found in object oriented programming.

Having difficulty reading these show notes via your podcast player? You can find this episode’s full show notes at http://www.codingblocks.net/episode67.

Sponsors

  • Linode – Use code “CODINGBLOCKS17” for $20 towards hosting (up to four months free!)

Survey says …

During Michael’s favorite portion of the show, this episode we ask: How many different podcasts do you subscribe to?

#yop-poll-container-44_yp59bf0e803168c { width: 1000; background:#fff; padding:10px; color:#555; overflow:hidden; font-size:12px; } #yop-poll-container-44_yp59bf0e803168c input[type='text'] { margin:0px 0px 5px 0px; padding:2%; width:96%; text-indent:2%; font-size:12px; } .yop-poll-name-44_yp59bf0e803168c { font-weight:bold; background:#327BD6; color:#fff; padding:5px; text-align:center; font-size:12px; } #yop-poll-questions-container-44_yp59bf0e803168c { font-size:14px; margin:5px 0px; } .yop-poll-question-container-44_yp59bf0e803168c { padding: 2px; } .yop-poll-question-44_yp59bf0e803168c { background:#327BD6; color:#fff; margin-bottom: 21px; margin-top: -10px; font-style: italic; text-align: center; width: 100%; padding:5px; } .yop-poll-answers-44_yp59bf0e803168c { } .yop-poll-answers-44_yp59bf0e803168c ul { list-style: none outside none; margin: 0; padding: 0; } .yop-poll-li-answer-44_yp59bf0e803168c { font-style:normal; margin:0px 0px 10px 0px; padding:0px; font-size:12px; margin-bottom:20px; } .yop-poll-li-answer-44_yp59bf0e803168c input { margin:0px; float:none; } .yop-poll-li-answer-44_yp59bf0e803168c label { margin:0px; font-style:normal; font-weight:normal; font-size:12px; float:none; } .yop-poll-results-44_yp59bf0e803168c { font-size: 12px; font-style: italic; font-weight: normal; margin-left: 15px; } .yop-poll-customs-44_yp59bf0e803168c { } .yop-poll-customs-44_yp59bf0e803168c ul { list-style: none outside none; margin: 0; padding: 0; } .yop-poll-li-custom-44_yp59bf0e803168c { padding:0px; margin:0px; font-size:14px; } /* Start CAPTCHA div style*/ #yop-poll-captcha-input-div-44_yp59bf0e803168c { margin-top:5px; } #yop-poll-captcha-helpers-div-44_yp59bf0e803168c { width:30px; float:left; margin-left:5px; height:0px; } #yop-poll-captcha-helpers-div-44_yp59bf0e803168c img { margin-bottom:2px; } #yop-poll-captcha-image-div-44_yp59bf0e803168c { margin-bottom:5px; } #yop_poll_captcha_image_44_yp59bf0e803168c { float:left; } /* End CAPTCHA div style*/ .yop-poll-clear-44_yp59bf0e803168c { clear:both; } #yop-poll-vote-44_yp59bf0e803168c { } /* Start Result bar*/ .yop-poll-results-bar-44_yp59bf0e803168c { background:#f5f5f5; height:10px; } .yop-poll-results-bar-44_yp59bf0e803168c div { background:#555; height:10px; } /* End Result bar*/ /* Start Vote Button*/ #yop-poll-vote-44_yp59bf0e803168c div#yop-poll-vote-44_yp59bf0e803168c button { float:left; } #yop-poll-vote-44_yp59bf0e803168c div#yop-poll-results-44_yp59bf0e803168c { float: right; margin-bottom: 20px; margin-top: -20px; width: auto; } #yop-poll-vote-44_yp59bf0e803168c div#yop-poll-results-44_yp59bf0e803168c a { color:#fff; text-decoration:underline; font-size:12px; } #yop-poll-vote-44_yp59bf0e803168c div#yop-poll-back-44_yp59bf0e803168c a { color:#555; text-decoration:underline; font-size:12px; } #yop-poll-vote-44_yp59bf0e803168c div#yop-poll-archive-44_yp59bf0e803168c a { color:#555; text-decoration:underline; font-size:12px; } #yop-poll-vote-44_yp59bf0e803168c div { float:left; width:100%; } /* End Vote Button*/ /* Start Messages*/ #yop-poll-container-error-44_yp59bf0e803168c { font-size:12px; font-style:italic; color:red; text-transform:lowercase; margin-bottom:20px; text-align:center; } #yop-poll-container-success-44_yp59bf0e803168c { font-size:12px; font-style:italic; color:green; margin-bottom:20px; text-align:center; } /* End Messages*/#yop-poll-container-44_yp59bf0e803168c img { max-width: 1000; } .yop-poll-forms-display{}
How many different podcasts do you subscribe to?
  • There can only be one. (Coding Blocks obviously)
  • Less than 10, YOLO
  • Less than 25, Cull the weak
  • Less than 50, Pro status
  • Less than 100, NPR is a second family to you

News

Object Oriented Mistakes

Anemic domain model

  • The use of the domain model without any business logic.
  • The domain model’s objects cannot guarantee their correctness at any moment, because their validation and mutation logic is placed somewhere outside (most likely in multiple places). Martin Fowler considers this to be an anti-pattern, but some disagree that it is always an anti-pattern.
  • Anemic domain models are contrary to the whole notion of object oriented design – data and business logic should be combined
  • You’ll typically see these things as standalone classes with other classes operating on them – aka a business layer or as Martin Fowler referred to as “transaction scripts”

Benefits

  • Clear separation between logic and data
  • Works well for simple applications
  • Allows for stateless logic – much easier to scale
  • Removes the need for a complex db / stateful mapping layer
  • Easier to use with dependency injection as constructors are simpler

Drawbacks

  • Logic cannot be implemented in an OO way
  • Violation of encapsulation and information hiding principles
  • Requires an outside class or layer to perform business logic
    • The side effect is the domain object cannot guarantee a “good” state at any point in time – it’s a dumb object
  • Typically needs some sort of service layer when being used between different applications
  • Makes the model less expressive

BaseBean

  • Inheriting functionality from a utility class rather than delegating to it
  • It’s not THAT you’re inheriting, it’s that you’re inheriting for the wrong reasons
  • You’re creating a game, and it has a car class with properties like “weight” and “topSpeed” and methods like “accelerate”. Later when you go to create a bullet class, you see that car already has the stuff you need – so you make your bullet inherit from your car…
  • Why is this bad? It’s confusing, and can cause problems later when new features are added (seatbelts) or when you try to logically treat things the same. GameObjects.Get<Car>().Accelerate()
  • Even worse, sometimes when you inherit you don’t need everything in that class…
  • How do you know if you’re walking down this path? Does inheriting feel “wrong”? Throwing NotImplimentedException?

Call super

  • https://en.wikipedia.org/wiki/Call_super
  • Requiring subclasses to call a superclass’s overridden method
  • How it works:
    • Base class has some cool method.
    • But not cool enough, so you need to override it in your subclass.
    • However, doing so *requires* that you call the super’s version.
      • It is this _requirement_ that is the anti-pattern
  • So how does this happen?
    • The base class may be assuming that a child class will augment some functionality or set some state that it relies on.
    • The base class may need to perform some setup operations for the class/framework to function correctly
      • Maybe these tasks are private?

“Whenever you have to remember to do something every time, that’s a sign of a bad API.”
Martin Fowlerhttps://www.martinfowler.com/bliki/CallSuper.html

Circle-ellipse / square-rectangle problem

  • Subtyping variable-types on the basis of value-subtypes
  • Violates the Liskov Substitution principle…
    • A subtype of a supertype should be substitutable for the super type.
      • Super Type Fruit, Sub Type Orange. The Orange should be able to be used anywhere type Fruit is used – ie you could substitute the Orange for Fruit

So where’s the problem?

  • Super type Ellipse
    • stretchX and stretchY
  • Sub type Circle
    • stretchX and stretchY would change a circle into an ellipse – changing the meaning of an object

Switch the two?

  • Super type Circle
    • Properties of radius and diameter
  • Sub type Ellipse
    • Cannot have meaningful properties of radius and diameter

So what’s a developer / engineer / programmer to do?

  • Change the model
    • Return boolean indicating success of the call
    • Throw an exception for an invalid method on the subtype
    • Return the resulting value after the method call
    • Allow for a weaker contract (stretchX and stretchY would simply change both the x and y values for a circle)
    • Modify the subtype to the super type on the change – could be very dangerous – expect some runtime exceptions!
    • Make everything immutable – calling the method returns a new object – similar to the problem above with type changes
    • Factor out modifiers – take anything that could mutate the ellipse and put it into an MutableEllipse class…yuck
    • Put preconditions on modifiers – Ellipse and Circle would then have a property called IsStretchable and upon calling stretch that would be called (similar to exception)
    • Factor out common functionality into an abstract class – now there is no inheritance
    • Drop inheritance relationships – Common operations factored out into interfaces that each class would then implement independently
    • Just merge the circle class into the ellipse – basically get rid of the circle class unless you have a specific need for it
    • Inverse inheritance – mutators are pushed up from the sub-class to the super-class
  • Use a different language or feature of a language
    • Using a language like LISP, you can change the class type without losing the object’s identity
  • Use a different paradigm – challenge the entire problem
    • Interesting analogy – Prisoner :: Person –> Person.walkNorth — Prisoner doesn’t have the freedom to do so
    • Rather than Circle and Ellipse, what about OneDiameterShape and TwoDiameterShape

Circular dependency

  • Introducing unnecessary direct or indirect mutual dependencies between objects or software modules
  • DLL hell example, Can’t upgrade A because it depends on B, which depends on C, which depends on A….
  • How can you tell? Can be tough, static analysis, or when you hit that first problem….
  • Can sometimes cause memory leak problems because the GC can’t cleanup
  • Often indicitive of flow/hierarchy problems where information would preferably flow in one direction (down)
  • Somewhat common and encouraged in functional programming?
  • What to do about it? Run static analysis tools like NDepend, beware of “reaching out”, and utilize patterns like IoC or Observers that cut concrete ties

Constant interface

  • Using interfaces to define constants
  • This is a Java thing.
  • Why would you be tempted to do this?
    • Maybe you’re thinking of composition over inheritance?
      • i.e. I need some constants but I can only inherit from one class. But I can implement many interfaces. Maybe?
    • You want a way to conveniently share a value across your app/namespace
  • Why is this bad?
    • A constant is an implemention detail. Now you’re implemation is leaking out of your class.
      • Think about all of the uses and subclasses of your this class that expose this constant.
    • Users don’t care about your constants
    • It’s part of the interface. Meaning it ‘s part of the contract. For a constant.
      • But the class implementing the constant could provide it’s own constant of the same name but with a different value. Now the “constant” has lost it’s meaning.
    • Some of this assumes that your class “implements” this interface.
      • In Java, you can make this interface act and seem more like an enum.
        • Which would make have a variable/reference to a type of this interface seem odd.

Resources We Like

Tip of the Week

Episode source