views:

292

answers:

8

I have a warehouse. Sometimes I want to lookup a box location by a name, sometimes by a description, sometimes by a UPC, maybe something else, etc. Each of these lookup methods call the same various private methods to find information to help locate the data.

For example, upc calls a private method to find a rowid, so does name, so does X. So I need to have that method for all of them. I might use that rowid for some way to find a shelf location (it's just an example.)

But my question is should I have an abstract class (or something else) because I am looking up my box in different ways.

In other words, say my code for lookups is very similar for UPC and for location. Each method may call something with (select * from xxxx where location =, or select * from xxxx where upc =). I could just create two different methods in the same class

LocateByUPC(string upc)...
LocateByLocation(string location)...
LocateByDescription(string description)

... again, this would be in one big class

Would there be any reason that I would want a super class that would hold

abstract class MySuper
{

properties...

LocateBox(string mycriteria)...

}

and then inherit that and create a second class that overrides the LocateBox method for whichever version I need?

I don't know why I'd want to do this other than it looks OOD, which really means I'd like to do this if I have a good reason. But, I know of no advantage. I just find that my class gets bigger and bigger and I just slightly change the name of the methods and a little bit of code and it makes me think that inheritance might be better.

Thank you. Using c# if that matters.

Edit - Would I do this if I only gave someone a .dll with no source but the class definition? The class def. would tell my properties, etc. and what methods to override.

A: 

Abstraction helps when you have multiple implementations. And for future-proofing (hoping that a newer implementation will crop up). An interface acts as a contract between the client and the implementer. This is an invariant. Implementations are free to add any number of methods they wish to. Do you have any such needs?

Does that help answer your question?

dirkgently
A: 

What you are proposing is (basically) the Strategy pattern. I don't like to link to wikipedia, but its a good place to start at least. Take a look at the pros and cons and see if it would be beneficial to you.

I don't think there's really a need for you to do it this way. You can simply make the LocateBox method public and have it call private helpers based on which search you want to do. It's generally a bad idea to overly complicate your class structure just for the sake of using some OO design principles. Wait until you find a need for them, and then refactor appropriately. This will help point out what is really necessary and what is a waste of your time.

Edit: Another approach that I was thinking of would be to create a data class that has properties based on the various things you could search by. Ie. a BoxSearchData class that has properties such as UPC, etc, and then pass that to LocateBox() and construct the query as necessary based on the properties that are null. This would help you construct searches on multiple criteria later down the line.

JoshJordan
+1  A: 

It sounds like you might want to implement the design pattern called Template Method. Basically you would define the outline of the lookup algorithm in a base class as final methods, placing common code in those methods. For the methods that require different behavior depending on the type, simply have the base class' final methods call protected methods in the children, and have each child type implement that behavior.

You can take a look online at some resources, just do a google search for Template Method design pattern. Hopefully it will shed some light on your question.

RibaldEddie
A: 

It wouldn't seem necessary in my opinion. Just have a single repository that has the different search functions. Then just use the functions you need when they're needed.

However, the interface portion would only become useful if you have tools that are queueing up different types of searches. Then you could have a factory creating different types of Search classes that all implement an Interface. At which point you could enumerate through your queued Search classes, cast to the interface, and execute the function which would be virtual and point to the correct search type. Example: ReturnDataObject GetItem(object param);

On a side note, there are other uses for interfaces when pulling data. That is just the first example that comes to mind.

regex
+3  A: 

Neither

neither using an abstract class nor an interface will simplify the protocol, i.e. you will still end up with a bunch of LocateXXX methods

I would recommend having a generic Locate(string criteria) method as the basis, and only defining specialized method signatures for the ones you know you will use frequently; the generic can be a catch-all for future expansion in case you need it (and relying on the generic simplifies coding and testing)

Steven A. Lowe
Yes, and to implement this I would suggest using the Template Method pattern. That way, you can remove duplicated code throughout the LocateXXX methods. In order to do this, you should create a class for each Locator type, as well as a base Locator class.
RibaldEddie
@[RibaldEddie]: in general, perhaps; in this specific example it's probably overkill - the specific methods like LocateByUpc(string upc) will internally just call the generic method Locate("UpcCode = '" + upc + "')... so no template is really required.
Steven A. Lowe
Actually, given the nature of the question "these lookup methods call the same various private methods" Template Method isn't overkill, it's one of the best ways to avoid code duplication in exactly this scenario. It's also the best way to maintain dependency injection capability in said scenario.
RibaldEddie
@[RibaldEddie]: i must be missing something here - LocateByUpc(string upc) { Locate("UpcCode = '" + upc + "'"); } - how can I use a Template Method (http://en.wikipedia.org/wiki/Template_method_pattern) on a one-line trivial construct like this? And what does it have to do with Dependency Injection?
Steven A. Lowe
I'm suggesting that he get rid of all of the LocateByXXX methods and replace them with classes that Locate(). The LocateXXX methods do mostly the same work, as the question says. So the point of template method is to put the same work in one place. RE: dep injection: base class calls child.
RibaldEddie
see: http://en.wikipedia.org/wiki/Hollywood_Principle Another reason to replace the LocateByXXX methods with classes is that the current design violates the Single Responsibility Principle.
RibaldEddie
I have read the template class material on a page for c#. I'm not sure I understand the benefit. I'd like to use it but for my case I don't know that it really helps anything. I'd like to know though...thanks.
johnny
I think I've tried to explain it as well as I can. If you don't understand how it works or why it would be useful, then don't use it.
RibaldEddie
@[RibaldEddie]: @[johnny]: if the Locate methods were more complicated, Template Methods would be appropriate; since in this case they reduce to a single statement (calling the generic), Template Methods are overkill. Not that they wouldn't work, and might support future expansion, they're YAGNI ;-)
Steven A. Lowe
I understad how they work. I don't understand the benefit here. I don't think there is one, but am willing to learn why. Perhaps if I were distributing a .dll and no source it might be good. They seem to be overkill here.
johnny
A: 

When in this example you look closely, you see that only the property, used for lookup, changes. When representing this in an OO way, you end up with a class I would call "Lookup" (representing a search, maybe in SQL, maybe in another query language: an object that can return a rowId based on some property and searched-for value of that property.

The real behavioral change would be in the query language. So if you are to create an abstract class or an interface, it should serve that purpose. The concern of variation in property and value can be separated by adding a "property" argument to the query call.

xtofl
A: 

An abstract class is useful when you need a substantial amount of functionality to be identical across the subclasses, for example in a shopping cart with multiple methods of payment available, you could have an abstract class which defines a generic payment method, and have subclasses inherit from the superclass for each actual payment method you want to support (paypal, credit card, account, etc). The mechanics of how a payment is authorized would be different for each subclass, but they all perform essentially the same function - they validate that a user can prove that they can pay for the goods or services in question.

An example of where an interface is useful is where you have unrelated items that need to provide some similar functionality in a uniform way. For example, you might have a CMS where articles are stored in a database, but where the system caches them to disc as well as HTML pages until the article in the database is modified, at which point the physical file is deleted until the next time someone access the copy in the database. Your CMS might also support the ability for users to upload images, PDFs, etc to be stored for access on the disc, but you definitely don't want these files to be deleted as the copy on the disc represents the file itself and not a cached version. In this case, you could create a Cacheable interface that says what methods a class which is cached to disc needs to implement, while leaving it up to the class itself to implement them. This makes more sense as classes that represent different kinds of data almost certainly need to implement their caching scheme (if any) differently.

Every class that allows caching would be defined as Class implements Cacheable, which is something you can then check for in your code. Less experienced coders might test the class of an object they are working with by getting the class and processing the result with a big switch statement. This isn't the correct approach because it means that you're assuming that certain classes objects implement certain functionality, and if you add a new class to the system you need to modify every switch statement in your software to take it into account. If yo uimplement an interface you can test if an object implements that interface with the instanceof keyword.

if ($thisObject instanceof Cacheable) { // Manage item's cache }

This approach is better because it eliminates the switch statement and thus makes your software easier to maintain. If you add a new class to the system that also implements its own caching scheme then you just need to declare that it implements Cacheable. As the interface requires all classes to implement it to declare the methods specified in the interface you can be sure that any class that implements Cacheable will provide certain methods for you to use. Your code doesn't need to know how the class implements these methods, just that it does implement them.

These concepts are somewhat trickier to explain than to actually learn to use I'm afraid, hopefully I've got the basic ideas across well enough for you to figure them out for yourself.

A: 

Obviously the entity that is polymorphic here is the constraint. Using string is the quick and dirty way of achieving the same but your type system is completely out of the loop and garbage string values will be just as valid for input as meaningful constraint specs.

So,

LocateBy (Constraint constraint);

and

abstract class Constraint { String toString (); }

class LocationConstraint extends Constraint { /* ... */}

etc.