tags:

views:

294

answers:

9

For example suppose I have a class Vehicle and I wish for a subclass ConvertibleVehicle which has extra methods such as foldRoof(), turboMode(), foldFrontSeats() etc. I wish to instantiate as follows

Vehicle convertible = new ConvertibleVehicle()

so I still have access to common method such as openDoor(), startEngine() etc. How do I designed such a solution?

To clarify my two initial solutions, neither of which I am happy with are:

  1. Have dummy methods foldRoof(), turboMode(), foldFrontSeats() which I override in ConvertibleVehicle only, leaving them to do nothing in other subclasses
  2. Have abstract methods foldRoof(), turboMode(), foldFrontSeats() and force each subclass to provide an implementation even if it will be blank in all instances other than ConvertibleVehicle

The above seem slightly convoluted since they both pollute the base class as I add an increasing number of subclasses each with their own unique functions

After reading some of the responses perhaps there is some type of fundamental flaw in my design. Suppose I have a class VehicleFleet which takes vehicles and instructs them to drive as follows:

public VehicleFleet(Vehicle[] myVehicles) {

    for (int i=0; i < myVehicles.length; i++) {
     myVehicles[i].drive();
    }
}

Suppose this works for dozens of subclasses of Vehicle but for ConvertibleVehicle I also want to fold the roof before driving. To do so I subclass VehicleFleet as follows:

public ConvertibleVehicleFleet(Vehicle[] myVehicles) {

    for (int i=0; i < myVehicles.length; i++) {
     myVehicles[i].foldRoof();
     myVehicles[i].drive();
    }
}

This leaves me with a messy function foldRoof() stuck in the base class where it doesn't really belong which is overridden only in the case of ConvertibleVehicle and does nothing in all the other cases. The solution works but seems very inelegant. Does this problem lend itself to a better architecture?

I'm using Java although I would hope that a general solution could be found that will work in any object oriented language and that I will not need to rely upon language specific quirks

+1  A: 

This is just what subclassing does: adds functionality not present in a base class.

class MyVehicle : public Vehicle {

public: 
  void MyNewFunction()
...

There are two (really just) different flavors of inheritance: public and private, reflecting the Is-A and Has-A relationships respectively. With public inheritance, you're directly adding stuff to a class. If I have class Animal with methods Eat() and Walk(), I may make a subclass called Cat which has the method Purr(). A Cat then has public methods Eat, Walk, and Purr.

In the case of a Stack based on a LinkedList however, I may say that a Stack HAS-A LinkedList internally. As such, I do not expose any features of the base class publically, I retain them as private and have to explicitly offer whatever I choose as public. A list may have a method Insert(), but for the Stack, I restrict the implementation and rexpose it as Push(). No previous public methods are exposed.

In C++, this is defined by the access modifier given before the base class. Above, I'm using public inheritance. Here, I use private inheritance:

class MyVehicle : private Engine {

This reflects that MyVehicle HAS-An Engine.

Ultimately, subclassing takes everything available in the base class and adds new stuff to it.

EDIT: With this new information it seems that you're really looking for, it seems, is interfaces as stated by an earlier (voted down) comment. This is one of the big problems with inheritance - granularity. One of C++'s big complaints is its implementation of multiple inheritance (an option to accomplish this.) Can you state specifically what language you're using so we can advise properly?

Tony k
public inheritance is NOT composition. Composition is orthogonal to inheritance. Composition is using an implementation class as a member.
caspin
Oops, my mistake. Corrected.
Tony k
A: 

This is pretty much basic inheritance. Have ConvertibleVehicle derive from Vehicle and you should be set.

jfclavette
+3  A: 

This is a good question. What it implies is that you have (or expect to have) code that asks a Vehicle to (for instance) foldRoof(). And that's a problem, because most vehicles shouldn't fold their roofs. Only code that knows it's dealing with a ConvertibleVehicle should call that method, which means it is a method that should be only in the ConvertibleVehicle class. It's better this way; as soon as you try to call Vehicle.foldRoof(), your editor will tell you it can't be done. Which means you either need to arrange your code so that you know you're dealing with a ConvertibleVehicle, or cancel the foldRoof() call.

Carl Manaster
+5  A: 

Any objects that use Vehicle shouldn't know about ConvertibleVehicle and its specialized methods. In proper loosely coupled object-oriented design Driver would only know about the Vehicle interface. Driver might call startEngine() on a Vehicle, but it's up to subclasses of Vehicle to override startEngine() to handle varying implementations such as turning a key versus pushing a button.

Consider reviewing the following two links which should help to explain this concept: http://en.wikipedia.org/wiki/Liskov_substitution_principle http://en.wikipedia.org/wiki/Open/closed_principle

Consider posting a real world problem that you feel leads to the dilemma you describe here and someone will be more than happy to demonstrate a better approach.

Kyle W. Cartmell
Good answer. The questioner is attemtping to violate OOD priciples which is a design flaw, most notably LSP.
vanslly
+1  A: 

I think most people are missing the point of Delta's question. It looks to me like he/she isn't asking about what inheritance is. He/She is asking about subclasses implementing functionality that is not a natural fit for a base class, and the resulting mess that can ensue. I.e. the pushing of specific methods / functionality up the hierarchy chain, or requiring that subclasses implement a contract for functionality that isn't a natural fit.

There is also the matter of whether it is valuable to be able to treat a base class like the subclass in every case (to avoid casting and use them interchangeably). *edit -- this is called the Liskov substitution principle (thanks for reminding me, Kyle).

Mark Simpson
A: 

How does the user of Vehicle know its a ConvertibleVehicle? Either they need to dynamic cast to ensure it is correct, or you've provided a method in Vehicle to get the objects real type.

In the first case the user already has a ConvertibleVehicle as part of dynamic cast. They can just use the new pointer/reference to access ConvertiblVehicle's methods

In the second case where the user verifies the objects type with one of Vehicles methods they can just cast the Vehicle to ConvertibleVehicle and use it.

Generally, casting is a bad idea. Try to do everything with the base class pointer. Your car example doesn't work well because the methods are too low level, build higher level virtual functions.

All that said. I have needed to all a derived classes methods from the base class. I could have cast to the derived class but it was involved in a framework and would have required much more effort. The old adage "all problems can be solved with one more layer of indirection" is how I solved this. I called a virtual method in the base class with the 'name' of the function I wanted to call. 'Name' can be a string or an integer depending on your needs. It's slower, but you should only need to do it rarely, if you class hierarchy is expressive enough.

caspin
+2  A: 

I've done this in similar situations.

Option A)

If the specialized operations are part of the same sequence as a base operation ( e.g. ConvertibleVehicle needs to be foldRoof before it can drive ) then just put the specialized operation inside the base operation.

class Vehicle { 
     public abstract void drive();
}

class ConvertibleVehicle { 
     public void drive() { 
         this.foldRoof();
         .... // drive 
     }
     private void foldRoof() { 
         ....
     }
 }

So the effect of driving a fleet will be some of them will fold their roof before being driven.

 for( Vehicle v : vehicleFleet ) { 
      v.drive();
 }

The specialized method is not exposed in the object public interface but is called when needed.

Option B)

If the specialized operation are not part of the same sequence and must be called under certain "special" circumstances then let a specialized version of a client call those specialized operations. Warning, this is not so pure nor low coupling but when both objects ( the client and the service ) are created by the same "condition" or builder then most of the times is ok.

class Vehicle { 
    public void drive() { 
        ....
    }
}
class ConvertibleVehicle extends Vehicle { 
         // specialized version may override base operation or may not.
        public void drive() { 
          ... 
         }

         public void foldRoof() { // specialized operation 
             ...
         }
 }

Almost the same as the previous example, only in this case foldRoof is public also.

The difference is that I need an specialized client:

// Client ( base handler ) 
public class FleetHandler { 
     public void handle( Vehicle [] fleet ) { 
           for( Vehicle v : fleet ) {  
               v.drive();
            }
     }
}

// Specialized client ( sophisticate handler that is )  
 public class RoofAwareFleetHandler extends FleetHandler { 
      public void handle( Vehicle [] fleet ) { 
           for( Vehicle v : fleet ) { 
              // there are two options.
              // either all vehicles are ConvertibleVehicles (risky) then
              ((ConvertibleVehicles)v).foldRoof();
              v.drive();

              // Or.. only some of them are ( safer ) .
              if( v instenceOf ConvertibleVehicle ) { 
                  ((ConvertibleVehicles)v).foldRoof();
              } 
              v.drive();
            }
       }
  }

That instaceof look kind of ugly there, but it may be inlined by modern vm.

The point here is that only the specialized client knows and can invoke the specialized methods. That is, only RoofAwareFleetHandler can invoke foldRoof() on ** ConvertibleVehicle**

The final code doesn't change ...

 public class Main { 
     public static void main( String [] args ) { 
         FleetHandler fleetHandler = .....
         Vehicles [] fleet =  ....

          fleetHandler.handle( fleet );
      }
 }

Of course, I always make sure the fleethandler and the array of Vehicles are compatible ( probably using abstrac factory or builder )

I hope this helps.

OscarRyz
+1  A: 

To add on to Kyle W. Cartmell's excellent answer, and to perhaps simplify Oscar Reyes's answer a tad...

You might want to consider having the base class define a method called prepareToDrive() where inherited classes could put any setup tasks that need to be done before starting up. Calling drive() would be the way to start everything up from the user's perspective, so we would need to refactor drive into a "setup" phase and a "go" phase.

public class Vehicle {
    protected void prepareToDrive() {
        // no-op in the base class
    }

    protected abstract void go();

    public final void drive() {
        prepareToDrive();
        go();
    }
}

Now, subclasses must implement the protected method go() (really bad method name, but you get the idea), which is where they do their class-specific handling of driving.

Now, your inherited class could look like this:

public class ConvertableVehicle extends Vehicle {

    // override setup method
    protected void prepareToDrive() {
        foldRoof();
    }

    protected void go() {
        // however this works
    }

    protected void foldRoof() {
        // ... whatever ...
    }
}

This structure would also help when you run into class TractorTrailerRig that needs to make sure the trailer is loaded and correctly attached before it can drive.

Ian McLaird
A: 

Having ConvertibleVehicle subclass Vehicle and add its own methods as you describe is perfectly fine. That part of the design is OK. The trouble you have is with fleet. ConvertibleFleet should not be a subclass of VehicleFleet. An example will show you why. Let's say VehicleFleet is like this:

public class VehicleFleet {
    // other stuff...

    public void add(Vehicle vehicle) {
        // adds to collection...
    }
}

This is perfectly fine and sensible, you can add any Vehicle or subclass of it to a VehicleFleet. Now, let's say we also have another kind of vehicle:

public class TruckVehicle extends Vehicle {
    // truck-specific methods...
}

We can also add this to a VehicleFleet since it's a vehicle. The problem is this: if ConvertibleFleet is a subclass of VehicleFleet, that means we can also add trucks to ConvertibleFleet. That's wrong. A ConvertibleFleet is not a proper subclass, since an operation that's valid for its parent (adding a truck) is not valid for the child.

The typical solution is to use a type parameter:

public class VehicleFleet<T extends Vehicle> {
    void add(T vehicle) {
        // add to collection...
    }
}

This will let you define fleets specific to certain vehicle types. Note that this also means there is no "base" VehicleFleet class that you can pass to functions that don't care what kind of vehicle the fleet has. This can be remedied using another layer of base class (or interface):

public interface VehicleFleetBase {
    Vehicle find(String name);
    // note that 'add' methods or methods that pass in vehicles to the fleet
    // should *not* be in here
}

public class VehicleFleet<T extends Vehicle> {
    void add(T vehicle) {
        // add to collection...
    }

    Vehicle find(String name) {
        // look up vehicle...
    }
}

For methods that are pulling vehicles out of fleets and don't care what kind they are, you can pass around VehicleFleetBase. Methods that need to insert vehicles use VehicleFleet<T> which is safely strongly-typed.

munificent