我经常做的PHP项目旨在从网页中删除分层数据并将其保存到数据库中(基本上,构建数据 - 考虑抓住拥有数据但不以结构化方式提供数据的政府网站)。每一次,我都试图提出一个OOP设计,让我能够实现以下目标:
到目前为止,我还没有找到解决方案,但最接近我得到的是这样的:
我为数据容器定义了一个抽象类,它将实现常见的树遍历函数:
abstract class DataContainer {
protected $parent = NULL;
protected $children = NULL;
public function getParent() {
return $this->parent;
}
public function getChildren() {
return $this->children;
}
}
然后我有实际的数据容器。想象一下,我正在将有关参加议会会议的数据下载到“坐在一起的具体问题”中。我会SessionContainer
,SittingContainer
,QuestionContainer
全部扩展DataContainer
。
每个会话,就座和问题数据都是从不同的URL中删除的。留下将URL内容放在一边的机制,我只想说我需要刮刀类,它将采用容器和DOmDocument进行实际解析。所以我会定义一个这样的通用接口:
interface Scraper {
public function scrapeData(DOMDocument $Dom, DataContainer $DataContainer);
}
然后,每个会话,坐着和问题都有自己的刮刀,它们实现了界面。但我也想确保他们只能接受他们想要的容器。所以它看起来像:
class SessionScraper implements Scraper {
public function scrapeData(DOMDocument $DOM, SessionContainer $DataContainer) {
}
}
最后,我会有一个通用的Factory
类,它也实现了Scraper接口,只是将刮擦分配给相关的刮刀。像这样:
public function scrapeData(DOMDocument $DOM, DataContainer $DataContainer) {
//get the scraper from configuration array
$class = $this->config[get_class($DataContainer)];
$craper = new $class();
$class->scrapeData($DOM, $DataContainer);
}
这是将在代码中实际调用的类。非常类似,我可以处理保存到DB - 每个数据容器都可以有DBSaver类,它将实现DBSaver接口。同样,所有调用都可以通过Factory
类完成,这也将实现DBSaver接口。
一切都很完美,但问题是实现接口的类应该实现接口的精确签名。例如。方法SessionScraper::scrapeData
无法接受仅 SessionContainer
个对象,它必须接受所有DataContainer
个对象。但这并不意味着!
最后,问题是:
instanceof
和类似检查的方法中强制执行类型,而不是通过类型提示强制执行?提前感谢所有建议/批评。如果有必要的话,我很高兴有人推翻这个代码!
答案 0 :(得分:2)
Container
涌入眼中。这个名字很通用,你可能需要更动态的东西。我认为你有Data
而你classify
,所以它有一个type
。
因此,您将完全接口硬编码到类型提示中,您应该动态解决此问题。
如果现在每个Container
都有type
,则Scraper
可以发出信号/告知它是否适用于type
Container
。
具体的抓取形式实际上是您用于解析特定数据的策略。您的容器封装此策略,为规范化数据提供接口。
您只需在Container
和Scraper
之间添加一些逻辑/合约,以便他们可以互相交谈。这个合约你可以放在两个界面内。
如果您想要展开多个Scraper
,也可以使用types
。
对于Container
,请查看SPL以及实现某些接口,以便可以使用迭代器(和递归迭代器)。这可能是您所指的通用结构,SPL可以提高Container
类的可用性。
您不需要在OOP中对所有内容进行硬编码,您可以保持动态,特别是在PHP中,您通常可以在运行时解决问题。
这也可以让您更轻松地用新版本替换Scrapers
。如
Scrapers
现在有一个定义类型(如上所述),你可以在运行时解决哪个具体类应该进行抓取,例如在一个漂亮的文件系统结构中从.php文件动态加载它们。
只需2美分。