tags:

views:

59

answers:

5

I've got a question about design. I have a class CodesTree that is building some sort of a binary tree according to data provided and the algorithm it's using needs a priority queue (nota bene also implemented using binary tree). There may be different implementations of a priority queue so I would like to allow the user of my class to choose the implementation he prefers. I designed the interface of the queue like this:

public interface IPriorityQueue<T>
{
        T GetMax();
        void Insert(T value);
        int Count { get; }
}

The question is - how do I allow the user to choose the implementation? The only way I can think of is to make a public property of a type IPriorityQueue in my CodesTree class and let user set this property to an instance of a class he wants to use. But I don't think it is a good idea, because the user may specify the queue that for example already has a few elements or, having a reference to the queue, delete some elements (and he shouldn't). It just seems a bit not elegant. What is the best way?

+2  A: 

What you are looking is called dependency injection. You can either pass an instance of the implementation in the constructor or use dependency injection frameworks out there to handle that.

Mehrdad Afshari
well yes i know that, but what about the part "But I don't think it is a good idea, because the user may specify the queue that for example already has a few elements or, having a reference to the queue, delete some elements (and he shouldn't). "?
agnieszka
You shouldn't care. You do this to give control to the user. All you should care is the implementation of the interface. What if the interface implementation deletes an element when its Insert method is called? It's not your problem. You give control, and as its consequence, responsibility to the user.
Mehrdad Afshari
well this is a very important answer for me because i've wondered about this matter many many times. thanks!
agnieszka
You have make a contract with the user in these cases. If the user uses the class in the given way, it will produce the documented results, otherwise the results are unpredictable.
Stefan Thyberg
@Stefan: Definitely, you should specify what the interface is expected to do in the documentation but you don't have to enforce anything in code.
Mehrdad Afshari
+2  A: 

Pass a priority queue factory to the CodesTree constructor.

public interface IPriorityQueueFactory<T>
{
    IPriorityQueue<T> CreatePriorityQueue();
}

Then the IPriorityQueueFactory<T> gets passed to the constructor.

This design uses the Factory Pattern and the Parameterize from Above pattern.

user9876
+1  A: 

One way I can think of is, use default template and if the user wants to use other priority queue, he can:

template < typename T, typename C = default_priority_queue<T>>
class CodesTree {};

Edit: I didn't realize we were in C#.

Try:

class CodesTree<T,C> where C : IPriorityQueue<T> {
}

I don't know if C can be defaulted, tho.

Anzurio
A: 

Your solution (the public IPriorityQueue property) is commonly seen when using "Dependency Injection" (or "Inversion of Control") frameworks and is known as "setter injection".

Another option would be "Constructor Injection", where the interface's implementation is provided as a constructor parameter.

There are many of these frameworks to choose from: .Net has Unity, Spring.Net, StructureMap Castle Windsor and others. Java has Spring and probably others as well.

These frameworks allow the user to specify the implementation he wants to use in an XML configuration file, or by providing a configuration at runtime to the Dependency Injection container.

When objects instances are created using such a framework, it takes care of creating/retrieving the dependency and providing them to the dependent object.

ckarras
+1  A: 

C#

Constrain the passed type of IPriorityQueue<> to new()-able types and create the thing local. This reduces your possibilities but is the most "secure" way. The user would have to pass a malicious implementation of IPriorityQueue<>.

public class CodesTree<T>
    where T: IPriorityQueue<Codes>, new()
{
    public CodesTree()
    {
        _queue = new T();
    }

    // ...

}

Runtime

If you can't or won't create the queue yourself, you can always explicitly check the requirements in the constructor:

public CodesTree(IPriorityQueue<Codes> queue)
{
    if (queue == null || queue.Count > 0)
        throw new ArgumentException("queue");
    _queue = queue;
}

This way is much more flexible as it allows arbitrarily constructed queues, but may lead to unexpected exceptions at runtime.

Checking the queue at run-time also enables you all the DI/IoC goodness others recommend in this question.

David Schmitt
i was looking for that! Tried to create new instance of the T type but got the information from visual studio that I can't.
agnieszka
It doesn't solve the fundamental problem. You can still create a subclass that gets initialized with some elements.
Mehrdad Afshari
Mehrdad said it right in a comment: "You shouldn't care. You do this to give control to the user. All you should care is the implementation of the interface. What if the interface implementation deletes an element when its Insert method is called? It's not your problem. You give control, and as its consequence, responsibility to the user."
David Schmitt
Exactly. I wasn't denying that fact. I was just pointing out that even `new()` constraint will not really make a big difference.
Mehrdad Afshari