【问题标题】:PHP OOP paradigm private arrayPHP OOP 范式私有数组
【发布时间】:2011-09-13 11:25:15
【问题描述】:

我在做的 API 架构的某些部分遇到了一些问题:

我有一个父类 - Vehicle
我有几个扩展Vehicle的类:CarTruckBicicleMotorcycle

我希望某些类有一个属性doors,它是一个Door 对象数组。 这将暗示一个私有变量$doors,以及方法getDoors()getDoor($id)addDoor(Door $door)removeDoor($id)

由于许多类没有doors,我不想在Vehicle 类中实现这一点,但我不想在所有具有doors 的类中重复自己。

所以我想在Vehicle 中使用魔术方法__call 和一个辅助类Statics,如下所示:

class Vehicle {
    ...
    public function __call($method, $args){
        if(!Statics::$method($this, $args)){
            throw new Exception('class ' . $this->get_class() . ' does not implement method ' . $method);           
        }
    }
}

class Car extends Vehicle {
    ...
    public $doors = array();
}

class Motorcycle extends Vehicle {
    ...
}

class Statics {
    function addDoor($vehicle, $args){
        try{
            array_push($vehicle->doors, $args[0]);
        } catch {
            return false;
        }
    }
}

这样,如果我们在一个Motorcycle对象中尝试addDoor方法,就会抛出异常。
这就像一个魅力,我只是有一个问题:

如何让Statics 类可以访问#doors 数组,但对程序员完全私有?

【问题讨论】:

    标签: php oop


    【解决方案1】:

    你的方法是正确的。您发现了标准继承模型不起作用的问题(除非您有多重继承支持)。在这种情况下,正确的答案是使用复合模型。

    您应该简单地创建一个“门”对象,而不是使用 Statics:: 类。这种 Static:: 方法的问题在于,如果需要,您不能将其子类化并覆盖特性。如果您将“门”变成一个对象,并在实例化任何需要它的车辆时创建它,您将保留 OOP 提供的灵活性。

    不过,这是一个非常抽象的问题,您可能已经打算这样做了。对于您所面临的实际设计问题,设计可能会更加明显。

    【讨论】:

    • 其实也可以是混合解决方案,添加“门”对象属性,添加静态“类”;-)
    【解决方案2】:

    最简单的解决方案是将Statics类的所有静态函数移动到抽象Vehicle类中,并将它们更改为protected。此外,将所有私有成员更改为受保护的。

    class Vehicle {
      ...
      protected function addDoor($args){
        try{
            array_push($this->doors, $args[0]);
        } catch {
            return false;
        }
      }
      ...
    }
    
    class Car {
      ...
      protected $doors = array();
      ...
    }
    
    class Motorcycle extends Vehicle {
      ...
    }
    

    这是一种倒置的逻辑,因为抽象基类知道其子类的内部结构,但奇怪的是它在 PHP 中也能工作。正确的 OOP 方法是创建受保护的抽象方法来访问这些成员。

    abstract class Vehicle {
      ...
      protected abstract function &GetDoors();
      protected function addDoor($args){
        try{
            array_push($this->GetDoors(), $args[0]);
        } catch {
            return false;
        }
      }
      ...
    }
    
    class Car {
      ...
      protected function &GetDoors(){ return $this->doors; }
      ...
    }
    
    class Motorcycle extends Vehicle {
      ...
      protected function &GetDoors(){ throw new Exception(); }
      ...
    }
    

    然而,你想要实现的(不再重复代码)可以直接使用 PHP 5.4 的新特性 Traits 来实现。也许你应该稍等一下......

    https://wiki.php.net/rfc/horizontalreuse

    【讨论】:

      【解决方案3】:

      您可以创建一个新类 VehicleWithDoor 来代替使用 __call,它扩展了 Vehicle,并包含您希望实现的门方法:

      class VehicleWithDoor extends Vehicle {
          protected $_doors = array();
      
          // door methods
      }
      
      class Car extends VehicleWithDoor {
      
      }
      

      ------------------------------------------

      编辑 2(我希望有更好的解决方案):

      你可以为你的属性添加和实现另一个接口

      interface Vehicle_Property {
           public function getIndex();
      }
      
      class Vehicle_Property_Doors implements Vehicle_Property {
           protected $_index = "doors";
      
           public function getIndex()
           {
                return (string)$this->_index;
           }
      
           public function open()
           {
      
           }
      
           public function close()
           {
      
           }
      }
      
      class Vehicle_Property_Wings {
           protected $_index = "wings";
      
           public function getIndex()
           {
                return (string)$this->_index;
           }
      
           public function fly()
           {
      
           }
      
           public function backToTheFuture()
           {
      
           }
      }
      
      class Vehicle {
          protected $_properties = array();
      
          public function addProperty(Vehicle_Property $property)
          {
               $this->_properties[$property->getIndex()] = $property;
          }
      
          public function removeProperty($key)
          {
               if (!array_key_exists($key, $this->_properties)
               {
                    return false;
               }
      
               return unset($this->_properties[$key]);
          }
      
          public function getProperty($key)
          {
               if (!array_key_exists($key, $this->_properties)
               {
                    return false;
               }
      
               return $this->_properties[$key];
          }
      
          protected function getProperties()
          {
              return (array)$this->_properties;
          }
      }
      

      【讨论】:

      • 这正是我想要的,我怎么没想到?谢谢
      • 该死,我现在有问题。门不是我唯一的属性,所以如果我有门和机翼,例如,我将不得不创建类 VehicleWithDoors、VehicleWithWings、VehicleWithDoorsAndWings ......如果我有三个或更多,甚至更糟......我在重复反正我自己
      • @André Alçada Padez:由于 PHP 的单一继承策略,这是行不通的。这就是在 PHP 5.4 中引入 traits 的确切原因。
      • 谢谢,我真的相信你,但正如你想象的那样,我在生产环境中,我真的不能等待标准化的 5.4。
      【解决方案4】:

      以前的解决方案,例如使用接口、将“门”限制为子类或复合类都可以。

      有时,在定义类层次结构时,您可能会在后代类中找到可能实现或未实现的特性(方法或属性)。我提出的解决方案是,将该特性添加到基类中作为“抽象或虚拟特性”,并让每个类决定是否覆盖。

      // check that in base classes, 
      // is common to have most stuff private or protected, not public
      class Vehicle {
          ...
      
          // using a protected variable field for properties
          protected $_doors = array();
          protected $_wings = array();
      
          // you may want to use the methods or the "__call" way,
          // Important, these are intentionally "protected", not "public"
          protected /* array */ getDoors()
          {
            return $this->_doors; 
          } // /* array */ getDoors(...)
      
          protected /* array */ setDoors(/* array */ p_doors)
          {
            $this->_doors = p_doors;  
          } // /* array */ SetDoors(...)
      
          protected /* void */ function addDoor(/* array */ $args)
          {
              array_push($this->doors, $args[0]);
          } // /* void */ function addDoor(...)
      
          // you may want to use the methods or the "__call" way,
          // Important, these are intentionally "protected", not "public"
          protected /* array */ getWings()
          {
            return $this->_wings; 
          } // /* array */ getWings(...)
      
          protected /* array */ setWings(/* array */ p_wings)
          {
            $this->_wings = p_wings;  
          } // /* array */ SetWings(...)
      
          protected /* void */ function addWing(/* array */ $args)
          {
              array_push($this->wings, $args[0]);
          } // /* void */ function addWing(...)
      
          // these one is always public in all classes
          public /* bool */ function supportsDoors()
          {
            return false;
          }
      
          // these one is always public in all classes
          public /* bool */ function supportsWings()
          {
            return false;
          }   
      } // class Vehicle
      
      class Car extends Vehicle {
          // these one is always public in all classes
          public /* bool */ function supportsDoors()
          {
            return true;
          }
      
          public /* array */ getDoors()
          {
            return $this->_doors; 
          } // /* array */ getDoors(...)
      
          // promoted from "protected" to "public"    
          public /* array */ setDoors(/* array */ p_doors)
          {
            $this->_doors = p_doors;  
          } // /* array */ SetDoors(...)
      
          // promoted from "protected" to "public"    
          public /* void */ function addDoor(/* array */ $args)
          {
              array_push($this->doors, $args[0]);
          } // /* void */ function addDoor(...)   
      } // class Car 
      
      class JetPack extends Vehicle {
          // these one is always public in all classes
          public /* bool */ function supportsWings()
          {
            return true;
          }   
      
          // promoted from "protected" to "public"    
          public /* array */ getWings()
          {
            return $this->_wings; 
          } // /* array */ getWings(...)
      
          // promoted from "protected" to "public"    
          public /* array */ setWings(/* array */ p_wings)
          {
            $this->_wings = p_wings;  
          } // /* array */ SetWings(...)
      
          public /* void */ function addWing(/* array */ $args)
          {
              array_push($this->wings, $args[0]);
          } // /* void */ function addWing(...)
      
      } // class JetPack
      
      
      class Boeing extends Vehicle {
      
          // these one is always public in all classes
          public /* bool */ function supportsDoors()
          {
            return true;
          }
      
          // these one is always public in all classes
          public /* bool */ function supportsWings()
          {
            return true;
          }   
      
      
          public /* array */ getDoors()
          {
            return $this->_doors; 
          } // /* array */ getDoors(...)
      
          // promoted from "protected" to "public"    
          public /* array */ setDoors(/* array */ p_doors)
          {
            $this->_doors = p_doors;  
          } // /* array */ SetDoors(...)
      
          // promoted from "protected" to "public"    
          public /* void */ function addDoor(/* array */ $args)
          {
              array_push($this->doors, $args[0]);
          } // /* void */ function addDoor(...)   
      
          // promoted from "protected" to "public"    
          public /* array */ getWings()
          {
            return $this->_wings; 
          } // /* array */ getWings(...)
      
          // promoted from "protected" to "public"    
          public /* array */ setWings(/* array */ p_wings)
          {
            $this->_wings = p_wings;  
          } // /* array */ SetWings(...)
      
          public /* void */ function addWing(/* array */ $args)
          {
              array_push($this->wings, $args[0]);
          } // /* void */ function addWing(...)
      
      } // class JetPack
      

      简历:“门”和“翼”在基类中被声明为“受保护的虚拟”,因此,所有后代类都将其设为受保护,但只有部分类实现了该功能, 并将功能、方法或属性公开。

      作为补充说明,我个人不喜欢使用 PHP 的“快速而肮脏的虚拟”属性和方法,而是使用显式的“getMyProperty”和“setMyProperty”方法,或者“myMethod()” , 因为是最佳实践。我建议避免使用这些常见的“_call”功能。_

      【讨论】:

        猜你喜欢
        • 2016-07-19
        • 2016-08-17
        • 1970-01-01
        • 2012-09-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-11
        • 2017-03-11
        相关资源
        最近更新 更多