如何创建一个对象,其派生类由创建属性隐式指定?
我正在寻找以下模式。 (我正在使用 Perl 工作,但我认为语言并不特别重要)。
有父类 Foo 和子类 Bar、Baz、Bazza。
构造 Foo 的方法之一是解析字符串,该字符串的一部分将隐式指定要创建哪个类。 例如,如果它以“http:”开头,那么它就是一个 Bar,但如果不是,但它包含“[Date]”,那么 Baz 喜欢它,依此类推。
现在,如果 Foo 知道它的所有子级,以及什么字符串是 Bar,什么是 Baz 等,它就可以调用适当的构造函数。 但基类不应该了解其子类的任何知识。
我想要的是 Foo 的构造函数能够依次尝试它的子级,直到其中一个说“是的,这是我的,我将创建这个东西”。
我意识到,在一般情况下,这个问题没有明确定义,因为可能有多个子节点接受该字符串,因此调用它们的顺序很重要:忽略这一点并假设字符串的特征使得只有一个子类会喜欢该字符串。
我想出的最好的方法是让子类在初始化时向基类“注册”,以便它获得构造函数列表,然后循环遍历它们。 但我缺少更好的方法吗?
示例代码:
package Foo;
my @children;
sub _registerChild
{
push @children, shift();
}
sub newFromString
{
my $string = shift;
foreach (@children) {
my $object = $_->newFromString(@_) and return $object;
}
return undef;
}
package Bar;
our @ISA = ('Foo');
Foo::_registerChild(__PACKAGE__);
sub newFromString
{
my $string = shift;
if ($string =~ /^http:/i) {
return bless(...);
}
return undef;
}
I'm looking for a pattern for the following. (I'm working in Perl, but I don't think the language matters particularly).
With a parent class Foo, and children Bar, Baz, Bazza.
One of the methods for constructing a Foo is by parsing a string, and part of that string will implicitly specify which class is to be created. So for example if it starts 'http:' then it's a Bar, but if it doesn't but it contains '[Date]' then Baz likes it, and so on.
Now, if Foo knows about all its children, and what string is a Bar, what is a Baz etc, it can call the appropriate constructor. But a base class should not have any knowledge about its children.
What I want is for Foo's constructor to be able to try its children in turn, until one of them says "Yes, this is mine, I'll create the thing".
I realise that in the general case this problem is not well-defined, as there may be more than one child which will accept the string, and so the order in which they are called matters: ignore this and assume that the characteristics of the string are such that only one child class will like the string.
The best I have come up with is for the child classes to 'register' with the base class on initialisation, so that it gets a list of constructors, and then loop through them. But is there a better method that I'm missing?
Sample code:
package Foo;
my @children;
sub _registerChild
{
push @children, shift();
}
sub newFromString
{
my $string = shift;
foreach (@children) {
my $object = $_->newFromString(@_) and return $object;
}
return undef;
}
package Bar;
our @ISA = ('Foo');
Foo::_registerChild(__PACKAGE__);
sub newFromString
{
my $string = shift;
if ($string =~ /^http:/i) {
return bless(...);
}
return undef;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
也许您可以使用 Module::Pluggable< 来实现此功能/a>? 这将消除注册的需要。
我之前采取的方法是使用 Module::Pluggable 加载我的子模块(这允许我通过简单地编写和安装它们来添加新的子模块)。 每个子类都有一个构造函数,该构造函数要么返回受祝福的对象,要么返回 undef。 您循环遍历插件,直到获得一个对象,然后返回它。
类似于:
有 Class:Factory也是如此,但这可能有点超出了您的需求。
Perhaps you could implement this with Module::Pluggable? This would remove the need for registration.
The approach I've taken before was to use Module::Pluggable to load my child modules (this allowed me to add new child modules by simply writing and installing them). Each of the child classes would have a constructor that either returned a blessed object or undef. You loop over your plugins until you get an object, then return it.
Something like:
There's Class:Factory as well but that may be a little over the top for your needs.
看来您正在尝试让一个类既是基类又是工厂。 不。 使用 2 个单独的类。 像这样的东西:
然后像这样使用它:
这样你的基类不需要知道你的子类,只有你的工厂需要知道。 并且子类也不需要相互了解,只有 Factory 需要知道要尝试哪些子类以及按什么顺序尝试的详细信息。
It seems you're trying to have a single class be both a base class and a factory. Don't. Use 2 separate classes. Something like this:
And then use it like:
This way your base class doesn't need to know about your child classes, only your factory does. And child classes don't need to know about each other either, only Factory needs to know the details of which child classes to try and in which order.
您可以在类 Foo 中实现任意查找算法,用于搜索现有的子类。 也许基于子类提供的配置文件,或者您可能想到的任何其他机制。
然后,Foo 类将在运行时检测现有的客户端类并依次调用它们。
此外,您可以缓存查找结果并接近您自己描述的注册表解决方案。
You may implement a arbitrary lookup algorithm in class Foo, that searches for existing child classes. Maybe based on config files provided with child classes, or with any other mechanism you might think of.
The class Foo will then detect the existing client classes at runtime and call them in turn.
Additionaly you may cache the lookup results and come close to the registry solution you already described yourself.
如果您对父类不包含有关子类的信息的评论以及将建立子类适合性的任务委托给类本身的方法,那么从父类中提取类选择并创建一个可能是正确的此任务的单例。
至少这是我的偏好......从此您当前的父类(可能在您的子类中具有一些通用功能)可能会变得抽象或接口。
然后,单例可以管理所有子类的构造及其分布(如果它们不起作用,则克隆它们?)...此外,可以将子类移动到单独的 dll 中以促进分离。
抱歉,这不是直接的解决方案。
我过去通过管理单例中的类列表来完成此操作,就像您在这里一样。 单例背后的想法是,如果您确实想使用任何昂贵的反射,则只需执行一次。
If you take your comment about the parent class not containing info about the chilidren and your method of delegating the task of establishing a child classes suitability to the class itself then it is probably correct to factor out the class selection from the parent class and create a singleton for this task.
at least that would be my preference ... from this your current parent class (which presumably has some common functionality across your child classes) can presumably then either become abstract or an interface.
the singleton could then manage the construction of all the child classes and their distribution (clone them if they are not functional?) ... moreover the child classes can be moved into a separate dll to promote separation.
sorry that is not a direct solution.
I have done this in the past by managing a list of classes in the singleton much like you are here. the idea behind the singleton is that if you do want to use any expensive reflection you only have to do it once.