views:

345

answers:

8

i am a newbie to the design patterns and here is my question

if we have an abstract class with few classes that implement it and each of this classes has different attributes.

now i have another (Manager class) that holds an array of the abstract class and i want to put a search method in it ... how can i do that without casting to the concrete classes ?

i have got 2 ideas:

First One : adding an extra levels of interfaces (ie. rather than casting to concrete class i will be casting to an interface ) which goes with the code to interface not implementation rule... but this way when i add another class i will have to make an interface for it and i will also have to edit the manager (client) , which doesnt seem very good.

Second Solution: it look somewhat strange and still needs enhancements but its main goal is to make the manager or any other client works with abstract class without knowing any thing about who extends it or its attributes.

the solutin is as folows : each new item added will have to overide one interface that enforces it to generate a complete discription of its fields for example a car object will have to return a hash map having the folowing

field : { fieldType , fieldValue }

example

  • model : { text , "ford" }
  • manifactureDate : { Date , "12/1/89" }

and each object will have also to implement a method called compareFields that take a hash map like this and compare it to its field and return true or false.

now by this way i have solved many issues -for the gui i will only have to make a rendering engine for this hashmap that can display any item without having to know what its type. (again the gui is another client for the abstract class)

-for the search i can get a hash map that contain the fields the user enters in the search form and loop on the abstract items and invoke the compare fieldmethod

i still don't how i will handle the complex object (that have another object as its attributes)

i dont know what kind of pattern is this .. it is just an idea that i thought about.

EDIT : CONCRETE EXAMPLE

if i have an abstract item Class with a car and bus and boat that implements it,,and each of this classes has different attributes.... how can a manager for example traffic manager search for a certain item using the abstract class without casting to car or bus... really sorry for the long question

+2  A: 

You seem to be describing what Steve Yegge calls the Universal design pattern.

Using key-value pairs with a GUI can work in simple cases, but it's hard to get it to look good. Unless your object hierarchy is deep and must be very extensible you may want to make a separate form for each concrete class, as this is less work and will look better. You can still reuse shared GUI components, so adding a new concrete class should be fairly simple.

RossFabricant
+5  A: 

Ok, so you're extending the class instead of implementing an interface. If you had classes Bus, Car, Truck, Train, and they all implemented IVehicle, that required a function that returned a sortable/searchable value, you would be able to reference all of them as type IVehicle and call that method on all of them.

ActionScript 3 Code:

package com.transportation.methods {
  public interface IVehicle {
    function getSpeed():Number;
    function getOtherSortableOrSearchableValue():*;
  }
}

and

public class Car extends Sprite implements IVehicle

You are required to define getSpeed() and getOtherSortableValue() in the Car class, and can refer to it as either a Car or an IVehicle. Since all modes of transportation in my example will implement IVehicle, as long as you are referencing them as IVehicles you can call those two functions.

Tegeril
this is my second method (the hash map method ) isnt it ?
Ahmed Kotb
Pretty much, yes.
Tegeril
This actually ignores one of the original stated restrictions, which is that each vehicle might have different attributes. So a car might have a getSpeed(), but perhaps a pogo stick wouldn't. A pogo stick might have a getBounceHeight(), but this makes no sense to a car. The hash map idea would handle these situations, but this particular answer would not.
Daniel Yankowsky
That's fine, a function can easily be implemented called compare that takes some kind of input that each class knows how to compare itself to, just because I didn't define that function in my answer doesn't mean it isn't possible.
Tegeril
It is also worth noting that the Visitor pattern marked as the accepted answer is not something that can be implemented in ActionScript 3 as you cannot define multiple functions with the same name - they would need to have different names and that breaks the original request for not needing to know which type is looked at, and as the original question did not specify a language, this is still a good solution given the language I selected.
Tegeril
@Tegeril Visitor pattern is possible in any language. If language is typed, then the method visit() can be differentiated based on the type of argument. In an untyped language, you need to name them visitTypeA(), visitTypeB(), etc. and change the accept() methods accordingly. See http://www.as3dp.com/2008/12/06/actionscript-30-visitor-design-pattern-a-tale-of-traverser-and-the-double-dispatch-kid/
ewernli
You've misread my comment. The visitor pattern -as defined in the accepted answer- cannot be done in AS3 because visit() can not be defined multiple times in AS3. The next part was that the original question required that manager not know which object was which, if for a Car you needed visitTypeA() and a truck visitTypeB() then the manager needs to know which is which. That's all I'm saying.
Tegeril
In hindsight, my comment is totally readable both ways, hopefully my last response cleared it up.
Tegeril
A: 

I don't see what language you're using, but why not have the abstract class implement the interface? That way, it won't matter how many concrete classes you have as long as they all inherit from the abstract class, which implements the interface.

Here's how the hierarchy would look if you are using Java:

public interface IVehicle {/*your code goes here*/}

public abstract class AbstractVehicle implements IVehicle{/*your code goes here*/}

public class Car extends AbstractVehicle{/*your code goes here*/}

These would all, of course, be defined in different files.

ssakl
if i added a new class for boat for example and it has attributes that is not comman with car and cant be added to vehicle interface... what can i do then ?
Ahmed Kotb
@Ahmed Kotb - One way of doing this would be to add a getAttributes() method to the interface which returns a HashMap containing key/value pairs of the attributes. Calling classes could then query the HashMap for which attributes (keys) exist, if need be. Your search() method could be defined in the abstract class to do this check before the actual search happens.
ssakl
yes that what i was trying to do in the second suggestion but i wanted to know if there was something better or if this was already a design pattern that i can read about
Ahmed Kotb
@Ahmed Kotb - The slight difference with your second suggestion is that your manager class could still work with just the interface, but the *implementation* is in the abstract class. You just need to design the interface with that search() method.
ssakl
A: 

how can a manager for example traffic manager search for a certain item using the abstract class without casting to car or bus

The way I'd do that is for the abstract class to have an abstract method that returns an ID value (say, an enum value) that indicates which concrete type the object is, and have each concrete class override that to return its ID value.

Or I'd try to use C++'s RTTI facilities, or Python's instanceof() function.

Does that help, or am I answering the wrong question?

Mike DeSimone
All of those are not-very OO-ish solutions and they break LSP.
Martinho Fernandes
yes i will do that when i have no other options as using instance-of means that the manager class have to know the concrete classes which i think not the best thing in oop as martinho said
Ahmed Kotb
+1  A: 

This seems to me like some sort of "centralized" Repository pattern. I don't think that's a good idea.

What you recommend you do is, instead of having one centralized way of generalized searching, you should have several specialized repositories: one for cars, one for boats, one for planes, etc.

Each of the repositories knows about the details of the respective objects. You can then factor out common operations between these specialized repositories, to a Repository base class or interface. When you don't care about the details, you use the base Repository interface. When you care about the details you use the specialized Repository.

Here's a simplistic example in Java (I'm leaving getters/setters out for shortness - don't make your fields public):

interface Vehicle { int id; int maxSpeed; }
class Car implements Vehicle { int doors; int id; int maxSpeed; }
class Boat implements Vehicle { int buoyancy; int id; int maxSpeed; }
class Plane implements Vehicle { int wingSpan; int id; int maxSpeed; }

interface VehicleRepository<T extends Vehicle> {
    T getFastest();
    T getSlowest();
    T getById(int id);
}

interface CarRepository inherits VehicleRepository<Car> {
    List<Car> getCarsWithTwoDoors();
}

interface BoatRepository inherits VehicleRepository<Boat> {
    Boat getMostBuoyantBoat();
}

interface PlaneRepository inherits VehicleRepository<Plane> {
    List<Plane> getPlanesByWingspan();
}
Martinho Fernandes
yes that was my first suggestion ... by creating an extra level of interfaces i think the only problem here when i add another item like rockets i will have to create a rocket repository right ?
Ahmed Kotb
Yes. And I don't think there's anything wrong with that, because you don't have to change existing classes to add rockets. If you use a centralized thing you'll have to change it everytime you add something else.
Martinho Fernandes
A: 

This sounds like a use case for the specification pattern.

http://en.wikipedia.org/wiki/Specification%5Fpattern

http://devlicio.us/blogs/casey/archive/2009/03/02/ddd-the-specification-pattern.aspx

Good luck!

Ben
+7  A: 

Encapsulation

The OO principle of encapsulation states that you object should not expose its state to the outside. If your object epxose it internal information it breaks the encapsulation. What is still OK according to OO design is to pass the search criterion to the object and let it decides whether they match.

interface IVehicle{
   bool doesMatch( Map<String,String> searchCriterion )
}

You can have an interator on all vehicles and retrieve the ones which match whithout breaking the encapsulation. Particular implementation of vehicles can still be reimplemented as desired.

Visitor

Otherwise, I would suggest you look at the Visitor pattern. The idea then is to traverse all the object and have an extra class handle the treatment for each specific type. This also breaks pure encapsulation (because the object need to expose its data to the visitor), but it's much elegant.

class VehicleSearchVisitor
{
   Map<String,String> searchCriterion;
   void visit( Car car ) {...}
   void visit( Bike bike ) { ... }
   ....
}

Meta-programming

The idea of object which are self-describing is another concept, which is called meta-programming. The presentation layer then introspect other object to know how to handle them. This traditionally is considered an advanced OO technique. You could could create custom annotations to describe the field of your class, so that the presentation layer can dynamically render the appropriate label. The same idea is for instance used with hibernate annotations. Meta-programming need to be done carefully otherwise you run into other issue.

Insteanceof

Use insteanceof is also a form of introspection (because you ask the object for its class) and is usually discouraged. Not because it is wrong in itself, but because it tend to be abused. Whenever possible, rely on traditional OO principles. Using instanceof abusively is a code smell.

All in all, I would recommend to use a visitor for the search, and to not use meta-programming for the presentation layer and to instead create one simple page per type of vehicle.

ewernli
I was going to recommend the first answer - it makes the most sense to me. This way, each IVehicle decides whether it matches the search criteria.
Daniel Yankowsky
+1  A: 

This sounds like a job for the Visitor pattern. You have a collection of abstract objects and you don't know what each one is. Using Visitor you can iterate over all the objects and perform an action specific to each one. The GOF Patterns book provides good detail on the Visitor pattern, but I'll try to provide a good Java example here.

public class Vehicle {
    public void accept( VehicleVisitor visitor ) {
        visitor.visit( this );
    }
}

public interface VehicleVisitor {
    public void visit( Vehicle vehicle );
    public void visit( Car car );
    public void visit( Bus bus );
    public void visit( Truck truck );
    // Augment this interface each time you add a new subclass.
}

public class Car extends Vehicle {
    public void accept( VehicleVisitor visitor ) {
        visitor.visit( this );
    }
}

public class Bus extends Vehicle {
    public void accept( VehicleVisitor visitor ) {
        visitor.visit( this );
    }
}

public class Truck extends Vehicle {
    public void accept( VehicleVisitor visitor ) {
        visitor.visit( this );
    }
}

public class VehicleSearch implements VehicleVisitor {
    protected String name;
    public List<Vehicle> foundList =
        new ArrayList<Vehicle>();
    public VehicleSearch( String name ) {
        this.name = name;
    }
    public void visit( Vehicle vehicle ) {
        return;
    }
    public void visit( Car car ) {
        if ( car.getModel().contains( name ) ) {
            foundList.add( car );
        }
    }
    public void visit( Bus bus ) {
        if ( bus.getManufacturerModel().contains( name ) ) {
            foundList.add( bus );
        }
    }
    public void visit( Truck truck ) {
        if ( truck.getLineModel().contains( name ) ) {
            foundList.add( truck );
        }
    }
}

public class Manager {
    protected List<Vehicle> vehicleList;
    public List<Vehicle> search( String name ) {
        VehicleSearch visitor =
            new VehicleSearch( name );
        for ( Vehicle vehicle : vehicleList ) {
            vehicle.accept( visitor );
        }
        return visitor.foundList;
    }
}

At first glance, yes you could simply add a search method to the Vehicle class and call that for each member of the list, but the visitor pattern allows you to define multiple operations over the list of vehicles such that you don't have to add a new method to the Vehicle class for each one.

One disadvantage of the Visitor pattern, though, is that the visitor itself needs to be changed when you add a new class of object being visited. In this example, if you add a RocketShip vehicle to the system, you will need to add a visit(RocketShip rocketShip) method to the visitor.

You can mitigate this issue by creating a VehicleVisitorTemplate with all the visit methods overridden. Your operations subclass the template class and then only need to override the needed methods. When you need to add a new class and visit method, you add it to the interface and the template class and then all your other operations don't need to be updated unless necessary.

David Smith