管理不同组件实现的控制器构造的设计问题
Design issues with a controller construct managing different component implementations
我正在思考一个相对简单的问题,但似乎找不到解决这个问题的方法 - 甚至有点老套...,我只是被卡住了,可以使用一些似乎不会流行的想法向上。下面显示的代码示例没有(完全)工作,而是用来显示我的问题。
我有一个控制器 class,有 3 种方法 init
、process
和 dispose
。控制器基本上是一个或多个要执行的所谓组件的包装器。
此外,还有一个 component_manager
class 来创建组件实例并管理它们的生命周期。组件管理器派生自定义了 4 个虚方法的接口,如 init
、prepare
、recycle
和 dispose
。
组件本身也有一个定义良好的接口icomponent
。所有组件都派生自该接口,并且所有组件都是默认可构造的(不需要参数)。
为了展示一些代码,我从我正在试验的代码中提取了重要部分,省略了“控制器”包装器 class 及其周围的所有噪音。
namespace detail {
// Iterator for std::tuple
template<typename Tuple, typename F, std::size_t ...Indices>
constexpr void for_each_impl(Tuple&& tuple, F&& f, std::index_sequence<Indices...>)
{
using swallow = int[];
(void)swallow{1,
(f(std::get<Indices>(std::forward<Tuple>(tuple))), void(), int{})...
};
}
} // Namespace detail
template<typename Tuple, typename F>
constexpr void for_each(Tuple&& tuple, F&& f)
{
constexpr std::size_t N = std::tuple_size<std::remove_reference_t<Tuple>>::value;
detail::for_each_impl(std::forward<Tuple>(tuple), std::forward<F>(f),
std::make_index_sequence<N>{});
}
// A component
class icomponent {
public:
virtual ~icomponent() = default;
virtual std::string id() = 0;
};
// Sample components a, b and c
class component_a : public icomponent {
public:
virtual std::string id() override { return "component a"; }
};
class component_b : public icomponent {
public:
virtual std::string id() override { return "component b"; }
};
class component_c : public icomponent {
public:
virtual std::string id() override { return "component c"; }
};
// Interface defining a component manager
class iprocessing_component_manager
{
public:
virtual ~iprocessing_component_manager() = default;
virtual T* prepare() = 0;
// More members like init(), recycle() and dispose()
};
// An simple, possible implementation of component manager
class simple_processing_component_manager
: public iprocessing_component_manager
{
public:
virtual ~simple_processing_component_manager() = default;
// ... other implemented methods ...
virtual T* prepare()
{
// ... creation of component
}
};
// In the controller wrapper, the process method
template<typename ...T>
void process()
{
// This initialization is normally within the controller wrapper
std::unique_ptr<simple_processing_component_manager> component_mgr = std::make_unique<simple_processing_component_manager>();
// Convert to tuple
std::tuple<T...> components;
// Iterate over (component) types ...
for_each(components, [&](auto& comp) {
using component_t = typename std::remove_reference<decltype(comp)>::type;
// Steps would be ...
// Instantiate the component (doesn't work, just showing what I need to accomplish)
component_t* x = component_mgr->prepare<component_t>();
// ^^^^^^^^^^^^^
// Perform component execution ... here I just print the id of the component
std::cout << x->id() << "\n";
// After processing component_mgr recycle() and / or dispose() are called
delete x;
});
}
将它们放在一起,典型的控制器用法如下所示:
controller c(/* some initialization parameters */);
result = c->process<component_a, component_c>(/* some more parameters */);
这将是我的首选使用方式,将组件作为可变模板参数传递。在控制器 process
方法中,这些组件类型被迭代和执行。我真的很想用 process
方法而不是控制器实例化来传递组件。
我的基本问题依赖于组件管理器 prepare
方法。 component_manager
及其 prepare
方法的简单实现如下所示:
T* prepare(/*...*/)
{
return new T(); // Create component instance
}
但是,我会有不同的组件管理器实现,例如,我想通过使用对象池来回收和重用创建的组件指针。在这种情况下,我可能会有
return borrow_object<T>(/* some key */); // Get from pool, will be returned back to pool with `recycle`...
A 最后,我将有各种不同的 component_managers
,并且 prepare
和其他 recycle/disposal 方法的每个实现都会有所不同,但是,对于所有情况,我都需要组件类型 T
。但是......模板方法不能是虚拟的。我知道这一点!
有什么方法可以解决上述问题,或者有更好的方法或设计来解决这个问题吗?我坐着盯着看这个太久了,所以如果我错过了一些非常简单的事情,请多多包涵。
感谢您的宝贵时间!
我不是继承和虚拟方法方面的专家...我不确定是否理解您的确切要求...无论如何,正如您所说,模板方法不能是虚拟的。
所以你必须考虑这个。
但是模板方法可以调用模板中的虚方法class。
所以我建议将 iprocessing_component_manager
中的虚拟 prepare()
设为 return 一个 icomponent
指针。
struct iprocessing_component_manager
{
virtual ~iprocessing_component_manager () = default;
virtual icomponent * prepare () = 0;
};
接下来,准备一个派生的单一类型模板 class 实现 prepare()
template <typename T>
struct type_processing_component_manager
: public iprocessing_component_manager
{
virtual ~type_processing_component_manager () = default;
virtual T* prepare () override
{ return new T; }
};
注意 prepare()
return 是指向 T
的指针,而不是指向 icomponent
.
的指针
现在您可以编写一个可变参数模板 tuple_processing_component_manager
,它继承自所有 type_processing_component_manager<Ts>
,并使用调用正确的模板(显然不是 virtual
)prepare()
(取决于类型)虚拟继承 prepare()
方法。
template <typename ... Ts>
struct tuple_processing_component_manager
: public type_processing_component_manager<Ts>...
{
virtual ~tuple_processing_component_manager () = default;
template <typename T>
T * prepare ()
{ return type_processing_component_manager<T>::prepare(); }
};
在主要 process()
中,您可以 component_mgr
基于可变处理组件管理器
auto component_mgr
{ std::make_unique<tuple_processing_component_manager<Ts...>>() };
并在 lambda 中使用它,如下所示
using component_t
= typename std::remove_reference<decltype(comp)>::type;
std::unique_ptr<component_t> x
{ component_mgr->template prepare<component_t>() };
std::cout << x->id() << "\n";
以下是完整的工作示例。
#include <tuple>
#include <memory>
#include <utility>
#include <iostream>
namespace detail
{
template <typename Tuple, typename F, std::size_t ... Is>
constexpr void for_each_impl (Tuple && tuple, F && f,
std::index_sequence<Is...>)
{
using swallow = int[];
(void)swallow { 1,
((void)f(std::get<Is>(std::forward<Tuple>(tuple))), 0)... };
}
}
template <typename Tuple, typename F>
constexpr void for_each (Tuple && tuple, F && f)
{
constexpr auto N
{ std::tuple_size<std::remove_reference_t<Tuple>>::value };
detail::for_each_impl(std::forward<Tuple>(tuple), std::forward<F>(f),
std::make_index_sequence<N>{});
}
struct icomponent
{
virtual ~icomponent () = default;
virtual std::string id () = 0;
};
struct component_a : public icomponent
{ virtual std::string id () override { return "component a"; } };
struct component_b : public icomponent
{ virtual std::string id () override { return "component b"; } };
struct component_c : public icomponent
{ virtual std::string id () override { return "component c"; } };
struct iprocessing_component_manager
{
virtual ~iprocessing_component_manager () = default;
virtual icomponent * prepare () = 0;
};
template <typename T>
struct type_processing_component_manager
: public iprocessing_component_manager
{
virtual ~type_processing_component_manager () = default;
virtual T* prepare () override
{ return new T; }
};
template <typename ... Ts>
struct tuple_processing_component_manager
: public type_processing_component_manager<Ts>...
{
virtual ~tuple_processing_component_manager () = default;
template <typename T>
T * prepare ()
{ return type_processing_component_manager<T>::prepare(); }
};
template <typename ... Ts>
void process ()
{
auto component_mgr
{ std::make_unique<tuple_processing_component_manager<Ts...>>() };
std::tuple<Ts...> components;
for_each(components, [&](auto& comp)
{
using component_t
= typename std::remove_reference<decltype(comp)>::type;
std::unique_ptr<component_t> x
{ component_mgr->template prepare<component_t>() };
std::cout << x->id() << "\n";
});
}
int main ()
{
process<component_a, component_b, component_c>();
}
如果你对components
的std::tuple
不感兴趣,在for_each()
处理中,你可以直接在递归[=]中使用type_processing_component_manager
结构29=]
// ground case
template <int = 0>
void process ()
{ }
// recursive case
template <typename T0, typename ... Ts>
void process ()
{
type_processing_component_manager<T0> component_mgr;
std::unique_ptr<T0> x { component_mgr.prepare() };
std::cout << x->id() << "\n";
// recursion
process<Ts...>();
}
我正在思考一个相对简单的问题,但似乎找不到解决这个问题的方法 - 甚至有点老套...,我只是被卡住了,可以使用一些似乎不会流行的想法向上。下面显示的代码示例没有(完全)工作,而是用来显示我的问题。
我有一个控制器 class,有 3 种方法 init
、process
和 dispose
。控制器基本上是一个或多个要执行的所谓组件的包装器。
此外,还有一个 component_manager
class 来创建组件实例并管理它们的生命周期。组件管理器派生自定义了 4 个虚方法的接口,如 init
、prepare
、recycle
和 dispose
。
组件本身也有一个定义良好的接口icomponent
。所有组件都派生自该接口,并且所有组件都是默认可构造的(不需要参数)。
为了展示一些代码,我从我正在试验的代码中提取了重要部分,省略了“控制器”包装器 class 及其周围的所有噪音。
namespace detail {
// Iterator for std::tuple
template<typename Tuple, typename F, std::size_t ...Indices>
constexpr void for_each_impl(Tuple&& tuple, F&& f, std::index_sequence<Indices...>)
{
using swallow = int[];
(void)swallow{1,
(f(std::get<Indices>(std::forward<Tuple>(tuple))), void(), int{})...
};
}
} // Namespace detail
template<typename Tuple, typename F>
constexpr void for_each(Tuple&& tuple, F&& f)
{
constexpr std::size_t N = std::tuple_size<std::remove_reference_t<Tuple>>::value;
detail::for_each_impl(std::forward<Tuple>(tuple), std::forward<F>(f),
std::make_index_sequence<N>{});
}
// A component
class icomponent {
public:
virtual ~icomponent() = default;
virtual std::string id() = 0;
};
// Sample components a, b and c
class component_a : public icomponent {
public:
virtual std::string id() override { return "component a"; }
};
class component_b : public icomponent {
public:
virtual std::string id() override { return "component b"; }
};
class component_c : public icomponent {
public:
virtual std::string id() override { return "component c"; }
};
// Interface defining a component manager
class iprocessing_component_manager
{
public:
virtual ~iprocessing_component_manager() = default;
virtual T* prepare() = 0;
// More members like init(), recycle() and dispose()
};
// An simple, possible implementation of component manager
class simple_processing_component_manager
: public iprocessing_component_manager
{
public:
virtual ~simple_processing_component_manager() = default;
// ... other implemented methods ...
virtual T* prepare()
{
// ... creation of component
}
};
// In the controller wrapper, the process method
template<typename ...T>
void process()
{
// This initialization is normally within the controller wrapper
std::unique_ptr<simple_processing_component_manager> component_mgr = std::make_unique<simple_processing_component_manager>();
// Convert to tuple
std::tuple<T...> components;
// Iterate over (component) types ...
for_each(components, [&](auto& comp) {
using component_t = typename std::remove_reference<decltype(comp)>::type;
// Steps would be ...
// Instantiate the component (doesn't work, just showing what I need to accomplish)
component_t* x = component_mgr->prepare<component_t>();
// ^^^^^^^^^^^^^
// Perform component execution ... here I just print the id of the component
std::cout << x->id() << "\n";
// After processing component_mgr recycle() and / or dispose() are called
delete x;
});
}
将它们放在一起,典型的控制器用法如下所示:
controller c(/* some initialization parameters */);
result = c->process<component_a, component_c>(/* some more parameters */);
这将是我的首选使用方式,将组件作为可变模板参数传递。在控制器 process
方法中,这些组件类型被迭代和执行。我真的很想用 process
方法而不是控制器实例化来传递组件。
我的基本问题依赖于组件管理器 prepare
方法。 component_manager
及其 prepare
方法的简单实现如下所示:
T* prepare(/*...*/)
{
return new T(); // Create component instance
}
但是,我会有不同的组件管理器实现,例如,我想通过使用对象池来回收和重用创建的组件指针。在这种情况下,我可能会有
return borrow_object<T>(/* some key */); // Get from pool, will be returned back to pool with `recycle`...
A 最后,我将有各种不同的 component_managers
,并且 prepare
和其他 recycle/disposal 方法的每个实现都会有所不同,但是,对于所有情况,我都需要组件类型 T
。但是......模板方法不能是虚拟的。我知道这一点!
有什么方法可以解决上述问题,或者有更好的方法或设计来解决这个问题吗?我坐着盯着看这个太久了,所以如果我错过了一些非常简单的事情,请多多包涵。
感谢您的宝贵时间!
我不是继承和虚拟方法方面的专家...我不确定是否理解您的确切要求...无论如何,正如您所说,模板方法不能是虚拟的。
所以你必须考虑这个。
但是模板方法可以调用模板中的虚方法class。
所以我建议将 iprocessing_component_manager
中的虚拟 prepare()
设为 return 一个 icomponent
指针。
struct iprocessing_component_manager
{
virtual ~iprocessing_component_manager () = default;
virtual icomponent * prepare () = 0;
};
接下来,准备一个派生的单一类型模板 class 实现 prepare()
template <typename T>
struct type_processing_component_manager
: public iprocessing_component_manager
{
virtual ~type_processing_component_manager () = default;
virtual T* prepare () override
{ return new T; }
};
注意 prepare()
return 是指向 T
的指针,而不是指向 icomponent
.
现在您可以编写一个可变参数模板 tuple_processing_component_manager
,它继承自所有 type_processing_component_manager<Ts>
,并使用调用正确的模板(显然不是 virtual
)prepare()
(取决于类型)虚拟继承 prepare()
方法。
template <typename ... Ts>
struct tuple_processing_component_manager
: public type_processing_component_manager<Ts>...
{
virtual ~tuple_processing_component_manager () = default;
template <typename T>
T * prepare ()
{ return type_processing_component_manager<T>::prepare(); }
};
在主要 process()
中,您可以 component_mgr
基于可变处理组件管理器
auto component_mgr
{ std::make_unique<tuple_processing_component_manager<Ts...>>() };
并在 lambda 中使用它,如下所示
using component_t
= typename std::remove_reference<decltype(comp)>::type;
std::unique_ptr<component_t> x
{ component_mgr->template prepare<component_t>() };
std::cout << x->id() << "\n";
以下是完整的工作示例。
#include <tuple>
#include <memory>
#include <utility>
#include <iostream>
namespace detail
{
template <typename Tuple, typename F, std::size_t ... Is>
constexpr void for_each_impl (Tuple && tuple, F && f,
std::index_sequence<Is...>)
{
using swallow = int[];
(void)swallow { 1,
((void)f(std::get<Is>(std::forward<Tuple>(tuple))), 0)... };
}
}
template <typename Tuple, typename F>
constexpr void for_each (Tuple && tuple, F && f)
{
constexpr auto N
{ std::tuple_size<std::remove_reference_t<Tuple>>::value };
detail::for_each_impl(std::forward<Tuple>(tuple), std::forward<F>(f),
std::make_index_sequence<N>{});
}
struct icomponent
{
virtual ~icomponent () = default;
virtual std::string id () = 0;
};
struct component_a : public icomponent
{ virtual std::string id () override { return "component a"; } };
struct component_b : public icomponent
{ virtual std::string id () override { return "component b"; } };
struct component_c : public icomponent
{ virtual std::string id () override { return "component c"; } };
struct iprocessing_component_manager
{
virtual ~iprocessing_component_manager () = default;
virtual icomponent * prepare () = 0;
};
template <typename T>
struct type_processing_component_manager
: public iprocessing_component_manager
{
virtual ~type_processing_component_manager () = default;
virtual T* prepare () override
{ return new T; }
};
template <typename ... Ts>
struct tuple_processing_component_manager
: public type_processing_component_manager<Ts>...
{
virtual ~tuple_processing_component_manager () = default;
template <typename T>
T * prepare ()
{ return type_processing_component_manager<T>::prepare(); }
};
template <typename ... Ts>
void process ()
{
auto component_mgr
{ std::make_unique<tuple_processing_component_manager<Ts...>>() };
std::tuple<Ts...> components;
for_each(components, [&](auto& comp)
{
using component_t
= typename std::remove_reference<decltype(comp)>::type;
std::unique_ptr<component_t> x
{ component_mgr->template prepare<component_t>() };
std::cout << x->id() << "\n";
});
}
int main ()
{
process<component_a, component_b, component_c>();
}
如果你对components
的std::tuple
不感兴趣,在for_each()
处理中,你可以直接在递归[=]中使用type_processing_component_manager
结构29=]
// ground case
template <int = 0>
void process ()
{ }
// recursive case
template <typename T0, typename ... Ts>
void process ()
{
type_processing_component_manager<T0> component_mgr;
std::unique_ptr<T0> x { component_mgr.prepare() };
std::cout << x->id() << "\n";
// recursion
process<Ts...>();
}