views:

168

answers:

8

I have the following piece of code:

if (book.type == A)  do_something();
else if (book.type == B) do_something_else();
....
else do so_some_default_thing.

This code will need to be modified whenever there is a new book type or when a book type is removed. I know that I can use enums and use a switch statement. Is there a design pattern that removes this if-then-else?

What are the advantages of such a pattern over using a switch statement?

+6  A: 

You could make a different class for each type of book. Each class could implement the same interface, and overload a method to perform the necessary class-specific logic.

I'm not saying that's necessarily better, but it is an option.

Kaleb Brasee
+2  A: 

Each different type of book is a different sub-class of the parent class, and each class implements a method do_some_action() with the same interface. You invoke the method when you want the action to take place.

Jonathan Leffler
A: 

You could define a subclass for each book type, and define a virtual function do_something. Each subclass A, B, etc would have its own version of do_something that it calls into, and do_some_default_thing then just becomes the do_something method in the base class.

Anyway, just one possible approach. You would have to evaluate whether it really makes things easier for you...

Justin Ethier
+1  A: 

Yes, it's called looping:

struct BookType {
    char type;
    void *do();
};
BookType[] types = {{A, do_something}, {B, do_something_else}, ...};
for (int i = 0; i < types_length; i++) {
    if (book.type == types[i].type) types[i].do(book);
}

For a better approach though, it's even more preferrable if do_something, do_something_else, etc is a method of Book, so:

struct Book {
    virtual void do() = 0;
};
struct A {
    void do() {
        // ... do_something
    }
};
struct B {
    void do() {
        // ... do_something_else
    }
};

so you only need to do:

book.do();
Lie Ryan
+1  A: 

Those if-then-else-if constructs are one of my most acute pet peeves. I find it difficult to conjure up a less imaginative design choice. But enough of that. On to what can be done about it.

I've used several design approaches depending on the exact nature of the action to be taken.

If the number of possibilities is small and future expansion is unlikely I may just use a switch statement. But I'm sure you didn't come all the way to SOF to hear something that boring.

If the action is the assignment of a value then a table-driven approach allows future growth without actually making code changes. Simply add and remove table entries.

If the action involves complex method invocations then I tend to use the Chain of Responsibility design pattern. I'll build a list of objects that each knows how to handle the actions for a particular case.

You hand the item to be processed to the first handler object. If it knows what to do with the item it performs the action. If it doesn't, it passes the item off to the next handler in the list. This continues until the item is processed or it falls into the default handler that cleans up or prints an error or whatever. Maintenance is simple -- you add or remove handler objects from the list.

Amardeep
+6  A: 

As others have pointed out, a virtual function should probably be your first choice.

If, for some reason, that doesn't make sense/work well for your design, another possibility would be to use an std::map using book.type as a key and a pointer to function (or functor, etc.) as the associated value, so you just lookup the action to take for a particular type (which is pretty much how many OO languages implement their equivalent of virtual functions, under the hood).

Jerry Coffin
A: 

Strategy Design Pattern is what I think you need.

Chubsdad
Most amusing that if you look in the example code for this article it uses an if-then-else-if construct in the setStrategy() method. :-)
Amardeep
A: 

As an alternative to having a different class for each book, consider having a map from book types to function pointers. Then your code would look like this (sorry for pseudocode, C++ isn't at the tip of my fingers these days):

if book.type in booktypemap:
     booktypemap[book.type]();
else
     defaultfunc();
Claudiu