PHP / OOP方法覆盖DRY方式 [英] PHP/OOP method overriding the DRY way

查看:112
本文介绍了PHP / OOP方法覆盖DRY方式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果以下行为有一个更好的设计,我很好奇:

 <?php 
class Foo {
public function foo(){
// Foo-foo stuff。
}
}

class Bar扩展Foo {
public function foo(){
//条特定的foo东西。
parent :: foo();
}
}

class Baz extends Bar {
public function foo(){
// Baz-specific foo stuff。
parent :: foo();
}
}

$ boz = new Foo();
$ boz-> foo(); //应该做的东西在Foo :: foo()

$ biz = new Bar();
$ biz-> foo(); //应该在Bar :: foo()和Foo :: foo()

$ buz = new Baz();
$ buz-> foo(); //应该在Baz :: foo(),Bar :: foo()和Foo :: foo()

// etc ...

本质上,我有一个基础类, Foo ,方法 Foo :: foo(),其中包含一些应该始终运行的常用代码。我还有各种子类继承自 Foo ,每个都有自己的特定代码,也应该始终运行。



我在这里使用的设计使用DRY原则来确保 Foo :: foo()中的代码在栏中不重复: :foo() Baz :: foo(),代码在 Bar :: foo() code>不在 Baz :: foo()中重复,等等。



这个设计的问题(?)是,我依赖子类来始终显式地调用 parent :: foo(),并将这些类扩展到做同样的事情,等等。然而,我没有办法(我知道)来实际执行这一点。



所以我的问题是 - 是否有更好的设计完成相同的行为,或一些在父/子班之间执行合约的方法?



更新



 <?php 
// Vehicle
class Vehicle {
public function start(){
//当你启动车辆引擎时。
//除非它们属于我,那就是:-(
$ this-> setEngineStatus(Vehicle :: ENGINE_ON);
}
}

//车辆>汽车
class汽车扩展车辆{
public function start(){
//当您启动汽车引擎时,
parent :: start ();

//启动时汽车空闲
$ this-> setEngineRpm(Automobile :: RPM_IDLE);
}
}

//车辆>飞机
class飞机延伸车辆{
public function start(){
//当您启动飞机引擎时,

$ this-> setRadioStatus(Airplane :: RADIO_ON);
($)

//启动时需要打开电台
}

//车辆>汽车>汽车
类汽车扩展汽车{
public function start(){
//汽车引擎在我和我当你开始他们时,
parent :: start();

//汽车也有仪表板灯,启动时打开。
$ this-> setDashLightsStatus(Car :: DASH_LIGHTS_ON);
}
}

//车辆>飞机> Jet
class Jet扩展Airplane {
public function start(){
//启动它们时,Jet引擎和收音机启动。
parent :: start();

//启动时喷枪也会武装武器。
$ this-> setWeaponsHot(true);
}
}

//车辆>汽车> BobsSuperAwesomeCustomTruck
class BobsSuperAwesomeCustomTruck扩展Automobile {
public function start(){
// Uh-oh ... Bob没有在他的课上调用parent :: start(),所以他的卡车
//不工作,没有错误或例外,以帮助他找出原因。

// Bob的卡车还需要在启动时重置弹球机高分。
$ this-> resetPinballScores();
}
}


解决方案

只要你在子类中覆盖你的方法,我知道的任何语言都没有办法强制父方法的行为。如果你正在为你的应用程序编写代码,你应该可以相信自己的代码来调用parent :: foo()。但是,如果您正在编写一个图书馆,框架或其他人将建立的API,那么您的想法是有价值的。 Ruby on Rails使用回调来很好地利用这种行为。



好的,所以不要定义任何 foo方法。而是使用__call和一个闭包数组作为回调。我的PHP真的很生锈,所以我忘了一些细节。

  class Foo {
//我忘了怎么在PHP中创建一个类变量,但这应该是一个。
//您可以根据需要定义任意多的回调链。
$ callbacks = array('foo_callback_chain'=> []);

//这应该是一个类函数。再次,忘了怎么样
函数add_callback($ name,$ callback){
$ callbacks [$ name .'_ callback_chain'] [] = $ callback;
}

//添加您的第一个回调
add_callback('foo',function(){
// do foo stuff
})

def method__call($ method,$ args){
//其实你可能会以相反的顺序调用它们,因为这样会更相似
foreach($ callbacks [$ method_name .'_ callback_chain'] as $ method){
$ method();
}
}
}

然后在你的孩子类中,只是添加更多的回调与add_callback这不适合所有的东西,但它在一些情况下工作得很好(更多关于闭包在 http://php.net/manual/en/functions.anonymous.php 。)


I'm curious if there is a "better" design for the following behavior:

<?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...

Essentially, I have a base class, Foo, with a method Foo::foo() that contains some common code that should always be run. I also have various subclasses which inherit from Foo and each have their own specific code that should also always be run.

The design I've used here uses the DRY principle to ensure that the code from Foo::foo() isn't repeated in Bar::foo() and Baz::foo(), and the code in Bar::foo() isn't repeated in Baz::foo(), and so on.

The problem(?) with this design is that I'm relying on the subclasses to always explicitly call parent::foo() in every case, and classes which extend those classes to do the same, and so on ad infinitum. However, there is no way (that I know of) to actually enforce this.

So my question is - is there a better design that accomplishes the same behavior, or some way to enforce this "contract" between parent/child classes?

Update

Some people have asked for a use-case. I have run into this paradigm in several projects over the years, but can't give a real world example due to NDAs and such, so here's a super basic example that might help illustrate the issue better:

<?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();
    }
}

解决方案

As long as you're overwriting your methods in subclasses, there is no way in any language I know of to enforce the behavior of the parent's method. If you're writing code just for your app, you should be able to trust your own code to call parent::foo(). But if you're writing a library, framework or API that others will build on, there is value to your idea. Ruby on Rails makes good use of that kind of behavior using callbacks.

Okay, so don't define any foo methods. Instead, use __call and an array of closures as callbacks. My PHP is really rusty, so I forget some of the specifics.

class Foo {
  // I forget how to make a class variable in PHP, but this should be one.
  // You could define as many callback chains as you like.
  $callbacks = array('foo_callback_chain' => []);

  // This should be a class function. Again, forget how.
  function add_callback($name, $callback) {
    $callbacks[$name.'_callback_chain'][] = $callback;
  }

  // Add your first callback
  add_callback('foo', function() {
    // do foo stuff
  })

  def method__call($method, $args) {
    // Actually, you might want to call them in reverse order, as that would be more similar
    foreach ( $callbacks[$method_name.'_callback_chain'] as $method ) {
      $method();
    }
  }
}

Then in your child classes, just append more callbacks with ""add_callback". This isn't appropriate for everything, but it works very well in some cases. (More about closures at http://php.net/manual/en/functions.anonymous.php.)

这篇关于PHP / OOP方法覆盖DRY方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆