jalone
jalone

Reputation: 2073

C++ construct if satisfies conditions

i have a design issue, not complicated actually, but i would like to find an elegant way to solve it. And i thought about this:

Issue: i have a class A that initialize and keep a collection of B

B is just an interface and must be implemented (so we have classes C,D,E,..).

in constructor A recive a bunch of dataset and must initialize some of B (also lot of different instantiation of same or different class) given each dataset. I would like A to not to know any implementation of B.

I have several working solution, but i was thinking about a kind of "delegate in the constructor". eg:

1. for each dataset, ds
2.   for each implementation of B, bi
3.     try to instantiate bi(ds)
4.       if success (means no exception)
5.         keep reference

this because the data and calculus i use to check if bi are pretty the same of initialization and being in a performance-critic application i would like to avoid doing that twice or doing it in collection class.
it would be really nice but obviously the problem is line 2...
...as well as the doubt about using exception for something that is not actually an exception. (line 4)
so what should be a pattern that
- let me evaluate data and construct all in one.
- avoid creating several "architectural-classes" i would like to avoid an explosion of classes ( kind of typical when exagerating with following design patterns java-style principles imho ) for such a simple task. - as fast as possible.
- ...is elegant :)

Upvotes: 5

Views: 199

Answers (3)

user1084944
user1084944

Reputation:

Your pseudocode suggests a solution: your use of bi is nothing more than a factory function that takes a dataset as input and returns a B* as output. So, you really just need to take bi from a collection of std::function<B* (dataset)> objects.

It would be easy enough to require that these factories are only 'conditional' factories: that sometimes they return valid objects, and sometimes they don't, returning nullptr instead. This would let you avoid exceptions, and is more faithful to the intent of your usage.

Upvotes: 1

Matthieu M.
Matthieu M.

Reputation: 299930

The simplest solution is to delegate to a Factory method.

std::unique_ptr<B> build(DataSet const& ds) {
    if (ds.property() == value) {
        return std::unique_ptr<B>(new D(ds));
    }
    return std::unique_ptr<B>(new C(ds));
}

Then A need only depend on the declaration of build.

Upvotes: 1

Bartek Banachewicz
Bartek Banachewicz

Reputation: 39380

The answer is based on my intuition right now, so it may not be perfect, but on the other hand, most of the design solutions aren't.

I would create just one additional class, call it factory or something similar. Then run all the constructions through this class. It should be able to be initialized with possible instantiations of derives of B either in run-time (by running callbacks at the beginning of the program), or, even better, by variadic template traits.

template<class ... RegisteredBImplementations>
class CFactory
{
    B* Create (dataset const& d)
    {
        // some magical meta-ifs to create compile time conditions
        // or for (auto& registered_type : registered types), and then choose one
        return new T(d);
    }
}

Then, A could use an instance of this class to initialize it's pointers properly:

for (auto& dataset : datasets)
{
    m_BPtrs.emplace_back( std::unique_ptr<dataset> (m_FactoryInstance.Create(dataset)) );
}

The main point of this solution is that class A effectively manages "B" objects, leaving proper construction of them to the other class. They are effectively separated, and addition of a new B implementation means a change only in CFactory, not in A.

Upvotes: 2

Related Questions