您的位置:首页 > 编程语言 > PHP开发

PHP:面向对象学习笔记,重点模拟Mixin(掺入)

2013-08-17 09:48 537 查看

背景

相对于Python、Node和Ruby来说PHP算是一门容易学习和使用的语言,因为这个特点也使其成为WEB开发领域的佼佼者,本文记录一下我对PHP面向对象部分的学习笔记。

先来一个复杂的例子:Mixin(掺入)

Ruby和Python可以用非常漂亮的语法支持掺入,PHP能实现吗?让我们试试吧。

参考其他语言的掺入示例可以查看这篇文章:设计原则:请重新审视“多重继承”,找机会拥抱一下“掺入(Mixin)”

PHP5.4提供的有Traits机制可以方便的模拟掺入,下面的示例是采用5.3版本的机制模拟出来的。

期望的最终效果

class Playable
{
public function play($that)
{
echo "一起玩吧,".$that->name;
}
}

class Base extends _Object
{
public $name = "段光伟<br/>";
}

class Child extends Base {}

Base::implement(new Playable());

$base = new Base();
$child = new Child();

$base->play();
$child->play();


是不是特别像C#的扩展方法。

实现代码

class _Object
{
private static $mixins = array();

public static function implement($target)
{
$class = get_called_class();

if (!isset(self::$mixins[$class]))
{
self::$mixins[$class] = array();
}

foreach (get_class_methods($target) as $method)
{
self::$mixins[$class][$method] = $target;
}
}

public function class_name()
{
return self::get_class($this);
}

public function __call($method, $params)
{
$params = array_merge(array($this), $params);
$class  = $class = get_called_class();

$mixin = $this->find_mixin($class, $method);

call_user_func_array($mixin, $params);
}

private function find_mixin($class, $method)
{
while ($class != NULL)
{
if (isset(self::$mixins[$class][$method]))
{
$target = self::$mixins[$class][$method];

return array($target, $method);
}

$class = get_parent_class($class);
}

throw new MethodException("方法 $method 不存在");
}
}


看不明白不要紧,继续看下文,回头再看这个。

面向对象

很多信息在注释中可以看到,在文中不会再重复一遍。

基本的类型声明

代码

<?php

header("content-type: text/html; charset=utf-8");

// 类型不能有修饰符。
// 成员以应用访问修饰符号,一共有三种访问修饰符:public、protected和private。
class TestClass {
// 属性默认访问级别是private。
private $private_property = "私共成员<br/>";

// var形式的默认访问级别是public。
var $public_property = "公共成员<br/>";

// 方法默认访问级别是public。
public function public_method() {
echo $this->private_property;
}
}

$test = new TestClass();

echo $test->public_property;
$test->public_method();

?>


运行结果

// 公共成员
// 私共成员


继承:实现继承和接口继承

代码

<?php

header("content-type: text/html; charset=utf-8");

/*
* 使用interface关键字可以声明接口,接口以及接口的成员不能有任何修饰符。
*/
interface Playable {
function play();
}

/*
* 使用abstract关键字可以声明抽象类和方法。
*/
abstract class Base {
private    $header = "";
private    $rooter = "";

public function __construct($header, $rooter) {
$this->header = $header;
$this->rooter = $rooter;
}

public function write() {
$this->writeHeader();
$this->writeContent();
$this->writeRooter();
}

private function writeHeader() {
echo $this->header."<br/>";
}

private function writeRooter() {
echo $this->rooter."<br/>";
}

protected abstract function writeContent();
}

/*
* 使用final关键字可以禁止类型或方法被重写。
* 使用parent::在重写的方法里调用父类型的方法。
*/
final class Child extends Base implements Playable {
private    $content = "";

public function __construct($header, $rooter, $content) {
parent::__construct($header, $rooter);

$this->content = $content;
}

protected function writeContent() {
echo $this->content."<br/>";
}

public function play() {
echo "游戏中。。。<br/>";
}

public function __destruct() {
echo "析构中<br/>";
}
}

$child = new Child("头", "尾", "内容");
$child->write("段光伟");
$child->play();

?>


运行结果

头
内容
尾
游戏中。。。
析构中


静态成员

代码

<?php

header("content-type: text/html; charset=utf-8");

class StaticBaseClass {
public static $StaticProperty = "父类静态属性<br/>";
const MAX = "父类常量<br/>";

public static function staticMethod() {
/* 在内部可以使用类名或self关键字,但是self访问的其定义时的类型,而不是运行时的类型,
* 这在子类和父类里有同名的静态成员和常量时会出现问题,为了避免这个问题可以使用static
* 关键字。
*/

echo get_called_class()." self::\$StaticProperty ".self::$StaticProperty;
echo get_called_class()." StaticBaseClass::\$StaticProperty ".StaticBaseClass::$StaticProperty;
echo get_called_class()." static::\$StaticProperty ".static::$StaticProperty;

echo get_called_class()." self::MAX ".self::MAX;
echo get_called_class()." StaticBaseClass::MAX ".StaticBaseClass::MAX;
echo get_called_class()." static::MAX ".static::MAX;
}
}

class StaticChildClass extends StaticBaseClass {
public static $StaticProperty = "子类静态属性<br/>";
const MAX = "子类常量<br/>";
}

// 在外部必须使用类名访问。
StaticBaseClass::StaticMethod();
echo StaticBaseClass::$StaticProperty;
echo StaticBaseClass::MAX;

// 在子类中可以调用父类的静态成员和常量。
StaticChildClass::StaticMethod();
echo StaticChildClass::$StaticProperty;
echo StaticChildClass::MAX;

?>


运行结果

StaticBaseClass self::$StaticProperty 父类静态属性
StaticBaseClass StaticBaseClass::$StaticProperty 父类静态属性
StaticBaseClass static::$StaticProperty 父类静态属性
StaticBaseClass self::MAX 父类常量
StaticBaseClass StaticBaseClass::MAX 父类常量
StaticBaseClass static::MAX 父类常量
父类静态属性
父类常量
StaticChildClass self::$StaticProperty 父类静态属性
StaticChildClass StaticBaseClass::$StaticProperty 父类静态属性
StaticChildClass static::$StaticProperty 子类静态属性
StaticChildClass self::MAX 父类常量
StaticChildClass StaticBaseClass::MAX 父类常量
StaticChildClass static::MAX 子类常量
子类静态属性
子类常量


又是魔法方法

代码

<?php

header("content-type: text/html; charset=utf-8");

/*
* 什么叫魔法方法:被解释器在某些特殊情况下调用的实例方法。
*/
class WebDeveloper {
public $info = array();

// 当执行赋值时,而目标成员不存在会调用此方法。
public function __set($item, $value) {
$this->info[$item] = $value;
}

// 当执行取值时,而目标成员不存在会调用此方法。
public function __get($item) {
return $this->info[$item];
}

// 当执行isset方法时,而目标成员不存在会调用此方法。
public function __isset($item) {
return isset($this->info[$item]);
}

// 当执行unset方法时,而目标成员不存在会调用此方法。
public function __unset($item) {
unset($this->info[$item]);
}

// 当执行方法调用时,而目标方法不存在会调用此方法。
public function __call($method_name, $args) {
echo $method_name, var_dump($args), "<br/>";
}
}

$developer = new WebDeveloper();
$developer->name = "段光伟";

echo "{$developer->name}<br/>";
echo (isset($developer->name) ? "TRUE" : "FALSE")."<br/>";
unset($developer->name);
echo (isset($developer->name) ? "TRUE" : "FALSE")."<br/>";

$developer->saySomething('hi!','how are you!');

?>


输出结果

段光伟
TRUE
FALSE
saySomethingarray(2) { [0]=> string(3) "hi!" [1]=> string(12) "how are you!" }


可调用对象(其实还是魔法方法)

代码

<?php
header("content-type: text/html; charset=utf-8");

class CallableClass
{
public function __invoke($x)
{
var_dump($x);
}
}

$obj = new CallableClass;

$obj(5);
call_user_func_array($obj, array(5));
var_dump(is_callable($obj));

?>


输入结果

int(5) int(5) bool(true)


Reflection(反射)

代码

<?php

header("content-type: text/html; charset=utf-8");

/*
* 使用interface关键字可以声明接口,接口以及接口的成员不能有任何修饰符。
*/
interface Playable {
function play();
}

/*
* 使用abstract关键字可以声明抽象类和方法。
*/
abstract class Base {
private    $header = "";
private    $rooter = "";

public static $StaticProperty = "父类静态属性<br/>";
const MAX = "父类常量<br/>";

public function __construct($header, $rooter) {
$this->header = $header;
$this->rooter = $rooter;
}

public function write() {
$this->writeHeader();
$this->writeContent();
$this->writeRooter();
}

private function writeHeader() {
echo $this->header."<br/>";
}

private function writeRooter() {
echo $this->rooter."<br/>";
}

protected abstract function writeContent();
}

/*
* 使用final关键字可以禁止类型或方法被重写。
* 使用parent::在重写的方法里调用父类型的方法。
*/
final class Child extends Base implements Playable {
public $content = "";
public static $StaticProperty = "子类静态属性<br/>";
const MAX = "子类常量<br/>";

public function __construct($header, $rooter, $content) {
parent::__construct($header, $rooter);

$this->content = $content;
}

protected function writeContent() {
echo $this->content."<br/>";
}

public function play() {
echo "游戏中。。。<br/>";
}

public function __destruct() {
echo "析构中";
}
}

$child = new Child("开始", "结束", "内容");

echo get_class($child).'<br/>';
print_r(get_class_methods(get_class($child)));
echo '<br/>';
print_r(get_class_vars(get_class($child)));
echo '<br/>';
echo $child->{"play"}();
?>


输出结果

Child
Array ( [0] => __construct [1] => play [2] => __destruct [3] => write )
Array ( [content] => [StaticProperty] => 子类静态属性
)
游戏中。。。
析构中


备注

PHP属于:静态类型、鸭子类型、弱类型、解释执行的语言 ,后面详细介绍这些概念及其对应的语言特色。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: