Thread: Why Scott Meyers wants you to slap yourself?

  1. #1
    Registered User
    Join Date
    Apr 2007
    Posts
    284

    Why Scott Meyers wants you to slap yourself?

    From Effective C++, by Scott Meyers :

    "Anytime you find yourself writing code of the form "if the object is of type T1, then do something, but if it's of type T2, then do something else," slap yourself.

    For example, why the following example(in JAVA) is not a good practice?

    Code:
    public final class BadInstanceOf {
    
      public static void doSomething(Animal aAnimal){
        if (aAnimal instanceof Fish){
          Fish fish = (Fish)aAnimal;
          fish.swim();
        }
        else if (aAnimal instanceof Spider){
          Spider spider = (Spider)aAnimal;
          spider.crawl();
        }
      }
    
      // PRIVATE //
      private static class Animal {}
    
      private static final class Fish extends Animal {
        void swim(){}
      }
      private static final class Spider extends Animal {
        void crawl(){}
      }
    }

  2. #2
    3735928559
    Join Date
    Mar 2008
    Location
    RTP
    Posts
    838
    because it reflects an inefficient design.

  3. #3
    Registered User
    Join Date
    Apr 2007
    Posts
    284
    inefficiency in terms of what?

    Quote Originally Posted by m37h0d View Post
    because it reflects an inefficient design.

  4. #4
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    Because that's not the important point of polymorphism.

    The point is to have code that uses the base class interface. Animal should have a method called move() that is overridden in the derived classes to crawl or swim as appropriate. The calling code should just call move().

    What if, later, you add a new derived class called Bird that extends Animal. You'd have to update the client code. If you designed your setup correctly, the client code would just work regardless of whether it was a fish, spider or bird and no changes would be required. In fact, the client code wouldn't even need to be recompiled.

  5. #5
    Registered User
    Join Date
    Oct 2001
    Posts
    2,129
    Additionally, in computational terms, it's not as scalable. If you do an if else for every possibility, it takes O(N) time, but if you use polymorphism, it would usually translate to a pointer dereference, and take O(1) time.

  6. #6
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Your abstract base class or interface should look at the 'behavior' of the object. Animals move, spider's move and fish move. I find it a stretch to call fish and spider animals but for simplicity I'll go with it.

    Code:
    class IAnimal
    {
       public:
        ...
        //Every animal can move
        virtual void Move() = 0;
    
        //Every animal can eat
        virtual void Eat() = 0;
        ...
    };
    
    class Fish:public IAnimal
    {
        public:
           ...
           virtual void Move()
           {
               //do some specific type of movement
           }
           virtual void Eat()
           {
              //eat in a specific manner
           }
           ...
    };
    The power in this type of design is that any object can be 'dropped in' as an animal as long as it inherits from IAnimal and implements the pure virtual functions it declares. At the very basic level of animal we don't care how it moves or eats. We care that it indeed does have those behaviors but we don't need to know any more about the how or the why of the behaviors. If a spider eats different than a fish that would be dealt with in the Fish impl and Spider impl of IAnimal.

    Another thing this buys you which is extremely nice is true polymorphism so that now you can have a list, vector, queue, map, etc. of IAnimal pointers at which point you could add fish, spider, and anything else that derived from IAnimal. Now you can iterate through the collection and call move or eat on the objects in the collection. The collection class knows nothing of the implementation details behind move or eat as they relate to each specific object. All the collection class knows is that to move or eat it can call those functions and then the object is responsible for the how it moves or eats.

    So now we can drop specific impls of IAnimal into the application with little effort. Nothing inside of the animal impls will affect the interface and therefore this makes each specific impl of IAnimal completely encapsulated and isolated. As long as they follow the intent of the interface they can function completely separate from any other object.

    Another example would be say for a sound engine in a game. There are tons of third party APIs out there that do sound. But from a very high level they all do the same things. They play sounds, stop sounds, load/unload sounds, add sound effects to sounds, etc. etc. So the interface would describe the very basic behavior of the sound system. Then you could implement the sound interface using DirectMusic, OpenAL, Ogg Vorbis, etc., etc. The power in this is you should literally be able to 'drop in' a new sound impl without changing anything in the interface. This means you could just load a different DLL for different impls of the sound interface and thereby could support many third party sound APIs yet care nothing about how they are implemented except from the standpoint of the impl - which in this case would probably act as a wrapper to the sound API.

    The less coupling there is between your objects the easier the code is to maintain and extend. The more coupling you have the more of a nightmare it becomes to do anything with.

    This is somewhat (not exactly) how modern graphics drivers interface with Direct3D. Microsoft has defined the interface that the driver manufacturers must implement. Because Microsoft could not possibly accurately support every video card out there they just define the general behavior of the API and leave the actual implementation to the pro's who designed the cards. ATI knows best how to code drivers for ATI cards and likewise for NVidia. The basic interface does not change (or usually does not change) but the underlying behavior might. If a bug shows up in a driver that implements the interface the interface does not have to be changed b/c it cares nothing about the implementation. This means the driver manuf. just releases new driver DLLs that have their underlying behavior changed but still adhere to the interface.

    In your example if we added any objects then we would have to add a specific impl of how the new object moves or eats. Your do something method would grow exponentially as you added new objects and new actions for said objects. This is extremely error prone and not easily extended or maintained. If you wanted to change how certain objects moved you would have to hunt for the code in your function, change it for each object that needed it, and then recompile. If you define a basc interface and use dynamic linking you would change the behavior of the underlying code, compile to a DLL, and then just link it in. Essentially you could patch your Spider or Fish movement by just releasing a new version of your Spider or Fish DLL. And this also means that if Fish is moving correctly but Spider is not...you just have to change the behavior in Spider::Move(). No nasty switch statements to pour through and no super-hacks involved to get it all working.
    Last edited by VirtualAce; 10-23-2008 at 11:56 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Scott Adams Has Stumped Me
    By Dante Shamest in forum A Brief History of Cprogramming.com
    Replies: 13
    Last Post: 10-28-2006, 09:22 AM
  2. Scott Peterson - Guilty
    By sean in forum A Brief History of Cprogramming.com
    Replies: 95
    Last Post: 11-15-2004, 11:21 PM
  3. The slap heard around the world
    By Thantos in forum A Brief History of Cprogramming.com
    Replies: 8
    Last Post: 03-26-2004, 03:50 PM
  4. Scott Meyers' Effective STL :: C++
    By kuphryn in forum C++ Programming
    Replies: 2
    Last Post: 10-12-2002, 11:12 PM