问题描述
|
在过去的两天里,我一直在研究PHP中的OOPS概念,我发现Abstract Class是一个非常有用的概念,我想在我的应用程序中实现它。这就是为什么我要实现它。
我的应用程序由几个非结构化模式组成,该模式使用了几个没有任何层次结构的类,因此我不得不使用几个重复的代码。我想将其全部切割并适当地构造,基本上我想要做的是
定义一个不能从外部实例化的父类。
在此父类中定义所有属性,以便我可以将同一属性用于不同的子类。
定义可重用的类方法和对象,以便子类可以使用它,而无需一次又一次地定义它(继承)。
基本上,我的Abstract类应该能够继承可重用的方法和属性,同时它还应充当接口。
为了清楚地演示您,让我向您展示一些我一直在使用的示例代码。
public $error = array(); //used to handle error mechanism and to hold the errors.
private $data = array(); //used with Accessor Methods (__set and __get)
private $validate; //holds Validate Object
private $dbh; //holds Database Object
public function __set($property,$value) {
if( in_array($property,$this->data)) {
return $this->data[$property] = $value;
} else {
return false;
}
}
public function __get($property) {
return \'Access Denied to Class Property [ \'.$property.\' ]\';
}
上面的代码几乎对每个类都重复,这就是我想在父类中定义一次并从那里控制机制的原因。
由于我仍然是许多OOP概念的新手,因此我无法理解如何使用Abstract Class实现我想要的东西。以下是我尝试使用哪种示例代码来声明抽象方法是错误的示例代码。
abstract class Property {
protected $error = array();
protected $data = array();
protected $dbh;
protected $validate;
abstract protected function connectDB($dbhandle) {
return $this->dbh = $dbhandle;
}
abstract protected function setValObj($valObj) {
return $this->validate = $valObj;
}
public function __set($property,$value) {
}
public function __get($property) {
}
}
这是我想做的。
子类启动时,应强制其定义抽象类中声明的方法。
Child类应该只能调用和传递争论,而不能扩展抽象方法。该机制应由父类处理。这就是我尝试在代码中执行的操作。
我知道我缺少某些东西,或者可能是我没有正确理解概念,有人可以向我解释我到底应该怎么做才能获得相同的结果。
解决方法
1。定义一个不能从外部实例化的父类。
所有抽象类都不能实例化,只能扩展。这就是您已经拥有的:
abstract class Property {
转到下一个:
2。在此父类中定义所有属性,以便我可以将同一属性用于不同的子类。
只需将它们写入该抽象类(通常也称为基类或抽象基类或模板类)。从基类扩展的所有类都可以访问protected
或public
成员方法和属性。如果您使变量或函数为“ 5”,则仅可用于基类中的代码。
因此,只需定义要在所有扩展类之间共享的基类中要具有的所有内容,而只需键入一次即可。
3。定义可重用的类方法和对象,以使子类可以使用它而无需一次又一次地调用它(继承)。
这会自动工作。从基类扩展时,可以通过扩展类自动访问其中定义的公共和受保护的类方法。您不需要(但是除非用final
指定,否则可以)不需要再次添加该功能以使其可用。例如。基类的所有公共方法都可以自动在其扩展的所有类上公开使用。
然后,您继续:
这是我想做的。
1。子类启动时,应强制其定义抽象类中声明的方法。
您可以通过将这些所需的方法定义为abstract
来实现。抽象方法需要由扩展类实现。但是,您不能将代码放入基类的抽象方法中。剩下的用于扩展类。
2。 Child类应该只能调用和传递争论,而不能扩展抽象方法。该机制应由父类处理。这就是我尝试在代码中执行的操作。
如果要防止子类覆盖函数,请在基类中将其声明为6。最终方法无法进一步扩展。
但可能您想做一些技术上不可能的事情,例如防止在您需要扩展方法的同时扩展该方法。
在代码中,您正在使用魔术函数来访问属性/值。从名字的意义上讲,这些不算在内。因此,在类设计的很大一部分中,您将失去对继承的控制。
但是,您可以实现数组访问以提供getter / setter。它绑定到一个具体的接口,然后您可以禁止通过基类进行访问,并防止从该基类扩展的类扩展该区域。
让我知道您是否想要一些示例代码,SPL可能对您也很新。
通过ArrayAccess提供变量继承
由于您已经遇到了无法在可用的魔术函数__get()和__set()上轻易使用继承的问题,我想到了使访问具体部分保持不变(获取(设置),同时仍然可以命名属性变量。 PHP可用的已经执行此操作的接口是ArrayAccess。它旨在通过标准php数组([]
)中已知的样式提供对属性的访问,并且通常用于此目的。但是对于此示例,它的好处是已经提供了适合一般需求的接口。
首先演示如何使用此类:
# give birth to the object
$object = new PropertyClass; // one of your property classes
# get:
$value = $object[\'propertyA\'];
# set:
$object[\'propertyA\'] = \'new value\';
# unset:
unset($object[\'propertyA\']); // and gone ;)
# isset:
isset($object[\'propertyA\']); // true / false
好的,正如所示,它看起来像一个数组,但是是一个对象。 ѭ11的其余部分按已知方式工作,因此这不是限制性的,而是附加的。
正如您已经可以用此代码想象的那样,还必须有一个get and set例程来读取和设置诸如__get()
和__set()
之类的属性值。另外,必须有一些用于isset和unset的东西,所以有四个。这是ArrayAccess的接口定义:
ArrayAccess {
/* Methods */
abstract public boolean offsetExists ( mixed $offset )
abstract public mixed offsetGet ( mixed $offset )
abstract public void offsetSet ( mixed $offset,mixed $value )
abstract public void offsetUnset ( mixed $offset )
}
您可以通过实现接口从PHP中扩展它。那不是extends
,而是implements
。这适用于PHP中的每个接口,但是该接口也很特别。它由SPL / PHP本身提供,在您的一类真正实现功能时,上面代码中描述的功能会自动添加到您的类中。
由于这些功能是公开可用的,因此您也可以自然地使用它们的名称来调用它们。
因此,实际上,此接口符合您要构建的属性对象的条件,并且会为您提供一个可以施加约束的接口。
因此剩下的唯一问题是:属性类的外观如何?
对变量属性类实现ArrayAccess
<?php
/**
* Property Object base class based on ArrayAccess
*/
abstract class PropertyObject implements ArrayAccess
{
/** Interface Methods */
/**
* implementing classes must return all names of their properties
* in form of an array.
*/
abstract protected function returnNames();
/** class */
/**
* value store
*
* @var array
*/
private $store = array();
/**
*
* By this design,properties can only contain A-Z and a-z.
*
* look like.
*
* @return bool
*/
private function isValidPropertyName($name) {
return ctype_alpha($name);
}
private function checkOffsetArgument($offset) {
if ($this->isValidPropertyName($offset)) return;
throw new InvalidArgumentException(sprintf(\'\"%s\" is not a valid property name.\',$offset));
}
private function setNames(array $names) {
foreach($names as $name) {
$this->checkOffsetArgument($name);
}
$len = count($names);
$this->store = $len
? array_combine($names,array_fill(0,$len,null))
: array()
;
}
/**
* final constructor to obtain control
*/
final public function __construct() {
$this->setNames($this->returnNames());
}
/**
* ArrayAccess impl.
*
* @return bool
*/
public function offsetExists($offset) {
$this->checkOffsetArgument($offset);
return array_key_exists($offset,$this->store);
}
/**
* ArrayAccess impl.
*
* @return mixed
*/
public function offsetGet ($offset) {
$this->checkOffsetArgument($offset);
return $this->store[$offset];
}
/**
* ArrayAccess impl.
*/
public function offsetSet($offset,$value) {
$this->checkOffsetArgument($offset);
if (!$this->offsetExists($offset)) {
throw new InvalidArgumentException(sprintf(\'Property \"%s\" can not be set.\',$offset));
}
$this->store[$offset] = $value;
}
/**
* ArrayAccess impl.
*/
public function offsetUnset($offset) {
$this->checkOffsetArgument($offset);
unset($this->store[$offset]);
}
}
/**
* I feel so concrete.
*/
class ConcreteType extends PropertyObject
{
protected function returnNames() {
return array(\'propertyA\');
}
}
$obj = new ConcreteType;
var_dump($obj[\'propertyA\']); # NULL,maybe you need other default values.
$obj[\'propertyA\'] = \'hello\';
var_dump($obj[\'propertyA\']); # string(5) \"hello\"
var_dump(isset($obj[\'propertyA\'])); # bool(true)
// this will trigger an exception
try {
$obj[\'XProperty\'] = \'good night.\';
} catch (Exception $e) {
var_dump($e->getMessage()); # string(36) \"Property \"XProperty\" can not be set.\"
}
// the following might be unwanted but can be prevented in base class:
unset($obj[\'propertyA\']);
var_dump(isset($obj[\'propertyA\'])); # bool(false)
,我认为您需要实现mixin接口。据我所知,PHP本身不支持此功能。一些PHP框架(例如Yii框架)是自己实现的。我在这里找到了一个例子。但是我相信您将能够找到更好的例子。