我很好奇是否有针对以下行为的“更好”设计:
<?php
class Foo {
public function foo() {
// Foo-specific foo stuff.
}
}
class Bar extends Foo {
public function foo() {
// Bar-specific foo stuff.
parent::foo();
}
}
class Baz extends Bar {
public function foo() {
// Baz-specific foo stuff.
parent::foo();
}
}
$boz = new Foo();
$boz->foo(); // should do the stuff in Foo::foo()
$biz = new Bar();
$biz->foo(); // should do the stuff in Bar::foo() and Foo::foo()
$buz = new Baz();
$buz->foo(); // should do the stuff in Baz::foo(), Bar::foo(), and Foo::foo()
// etc...
本质上,我有一个基类 Foo,它有一个方法 Foo::foo(),其中包含一些应该始终运行的通用代码。我还有各种继承自 Foo 的子类,每个子类都有自己的特定代码,这些代码也应该始终运行。
我在这里使用的设计使用 DRY 原则来确保 Foo::foo() 中的代码不会在 Bar::foo() 中重复> 和 Baz::foo(),Bar::foo() 中的代码在 Baz::foo() 中没有重复> 等等。
这种设计的问题(?)是我依赖于子类在每种情况下总是显式调用 parent::foo(),以及扩展这些类的类来执行同理,以此类推。但是,(据我所知)没有办法实际执行此操作。
所以我的问题是 - 是否有更好的设计来实现相同的行为,或者是否有某种方式在父类/子类之间强制执行这种“契约”?
更新
有些人要求提供用例。多年来我在几个项目中遇到过这种范例,但由于 NDA 等原因无法给出真实世界的例子,所以这里有一个 super 基本的例子,可能有助于更好地说明这个问题:
<?php
// Vehicle
class Vehicle {
public function start() {
// Vehicle engines are on when you start them.
// Unless they belong to me, that is :-(
$this->setEngineStatus(Vehicle::ENGINE_ON);
}
}
// Vehicle > Automobile
class Automobile extends Vehicle {
public function start() {
// Automobile engines are on when you start them.
parent::start();
// Automobiles idle when you start them.
$this->setEngineRpm(Automobile::RPM_IDLE);
}
}
// Vehicle > Airplane
class Airplane extends Vehicle {
public function start() {
// Airplane engines are on when you start them.
parent::start();
// Airplanes also have radios that need to be turned on when started.
$this->setRadioStatus(Airplane::RADIO_ON);
}
}
// Vehicle > Automobile > Car
class Car extends Automobile {
public function start() {
// Cars engines are on and idle when you start them.
parent::start();
// Cars also have dashboard lights that turn on when started.
$this->setDashLightsStatus(Car::DASH_LIGHTS_ON);
}
}
// Vehicle > Airplane > Jet
class Jet extends Airplane {
public function start() {
// Jet engines and radios are on when you start them.
parent::start();
// Jets also arm their weapons when started.
$this->setWeaponsHot(true);
}
}
// Vehicle > Automobile > BobsSuperAwesomeCustomTruck
class BobsSuperAwesomeCustomTruck extends Automobile {
public function start() {
// Uh-oh... Bob didn't call parent::start() in his class, so his trucks
// don't work, with no errors or exceptions to help him figure out why.
// Bob's trucks also need to reset their pinball machine highscores when started.
$this->resetPinballScores();
}
}
最佳答案
我不认为这更好,但这是一种可能的方式。
class abstract Foo {
public function foo() {
// Foo-specific foo stuff.
$this->_foo();
}
// Might be abstract, might be an empty implementation
protected abstract function _foo();
}
class Bar extends Foo {
protected function _foo() {
// Bar-specific foo stuff.
}
}
就个人而言,我更喜欢你的方式,因为我认为它更具可读性。这也意味着 child 不必拥有自己的 foo() 实现。似乎更面向对象。但是,如果您需要每个子类都有自己添加的 foo() 实现,这可能会为您解决问题。
关于PHP/OOP 方法覆盖 DRY 方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7394986/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
这似乎应该有一个直截了当的答案,但在Google上花了很多时间,所以我找不到它。这可能是缺少正确关键字的情况。在我的RoR应用程序中,我有几个模型共享一种特定类型的字符串属性,该属性具有特殊验证和其他功能。我能想到的最接近的类似示例是表示URL的字符串。这会导致模型中出现大量重复(甚至单元测试中会出现更多重复),但我不确定如何让它更DRY。我能想到几个可能的方向...按照“validates_url_format_of”插件,但这只会让验证干给这个特殊的字符串它自己的模型,但这看起来很像重溶液为这个特殊的字符串创建一个ruby类,但是我如何得到ActiveRecord关联这个类模型
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco
我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah