1 文法
1.1 基本文法
クローン
元のオブジェクトを操作する必要があるが、元のオブジェクトには影響を与えたくない。
基本的なデータ型と配列は両方とも真のコピーです。つまり、属性がオブジェクトの場合、コピーを変更しても元のオブジェクトに影響します。
//在原对象中添加 function __clone(){ $this->对象 = clone $this->对象 }
2. 参照を渡す
メソッド参照の受け渡し、ソースオブジェクトの変更
アプリケーション シナリオ: Dog クラスと Person クラスの両方には、インスタンス化を返すメソッドが必要です。Dog クラスと Person クラスは両方とも Animal 抽象クラスから継承します。
abstract class Animal{ public static function create(){ //实例化调用类 return new static(); } } class Person extends Animal{...} //返回Person实例化类 Person::create();
__get($property)、未定義のプロパティにアクセスするときに呼び出されます。
__set($property,$value)、未定義のプロパティに値を割り当てるときに呼び出されます。
__isset($property)、未定義のプロパティで isset() メソッドを呼び出すときに呼び出されます。
__unset($property)、未定義のプロパティで unset() メソッドを呼び出すときに呼び出されます。
__call($method,$arg_array)、未定義のメソッドを呼び出すときに呼び出されます。
__call は非常に便利ですが、柔軟性が高すぎるため、使用には注意してください。
アプリケーション シナリオ: Person_Writer クラスが Person クラスを通じて呼び出された場合、特別に Person クラス情報を出力する Person_Writer クラスがあります。
//Person委托Person_Writer类处理打印事务. class Person { private $writer; ... function __call($method_name,$args){ if(methood_exists($this->wirter,$method_name)){ return $this->writer->method_name($this); } } //高级__call写法,当委托方法参数不确定时使用. function __call($method_name,$args){ //当然这里这样写法意义不大,但是call一般都是用call_user_func_array调用 $args = $this ; if(methood_exists($this->wirter,$method_name)){ return call_user_func_array( array($this->writer,$method_name),$args); ) } } }
適用シナリオ:Productクラス、Product_Saleクラス、Product_Totalizerクラスの3クラスを実装:Productの販売合計金額が指定金額を超えた場合に警告を出力します。
//Product class Product { public $name; public $price; } //Product_Sale class Product_Sale { private $callbacks; //记录回调函数 function register_callback ($callback) { if(! is_callback($callback)){ thow new Exception('callback not callable'); } $this->callbacks[] = $callback; } //执行回调函数 function sale ($product){ print "{$product->name} : 处理中 \n"; foreach($this->callbacks as $callback){ call_user_func($callback , $product); } } } //Produce_Totalizer class Produce_Totalizer { static function warn_amount ($amt) { $count = 0; return function ($produce) use ($amt , &count) { $count += $produce->price; print " count : {count}\n" if($count>$amt){ print "超过指定金额{$amt}啦~"; } }; } } //模拟场景 $product_sale = new Produce_Sale(); //指定报警金额为8块 $product_sale = register_callback(Produce_Totalizer::warn_amount(8)); //卖商品 $product_sale->sale(new Product("Durex",6)); $product_sale->sale(new Produce("Jissbon",5)); //输出结果 Durex : 处理中 count :6 Jissbon : 处理中 count: 11 超过指定金额8块啦~
get_class(class) は、クラス名と正確に等しいかどうかを判断するために使用されます。
instanceof は、それ自体であるか、親クラスから継承しているかを判断できます。
7. クラス内のメソッドとクラス内の属性
2モード
2.1 組み合わせ問題: クラスルームクラスは講義クラスとセミナークラスに継承されますが、講義クラスとセミナークラスの両方で、一括課金方式と出力計算方法を実装する必要があります。
解決策 1: 1 回の支払いを計算するメソッド、N レッスン分の料金を請求するメソッド、クラス クラスに計算方法を出力するメソッドを追加します。
解決策 2: 組み合わせを使用して、請求処理メソッドと出力計算メソッドを個別に請求戦略クラスにカプセル化します。
abstract class Cost_Strategy { protected $duration; abstract function cost (); abstract function charge_type(); public __construct($duration){ $this->duration = $duration; } } class Timed_Const_Strategy extends Cost_Stratedy { function cost () { //上一次课给5块钱- -. return $this->duration * 5; } function charge_type(){ return "多次课结算"; } } class Fixed_Const_Strategy extends Cost_Stratedy { function cost (){ return 30 ; } function charge_type(){ return "一次性课结算"; } } abstract class Leason { private $cost_strategy; public __construct(Const_Strategy $cost_strategy){ $this->cost_strategy = $cost_strategy; } function __call($method_name,$args){ $args = $cost_strategy ; if(methood_exists($this->cost_strategy,$method_name)){ return call_user_func_array( array($this->writer,$method_name),$args); ) } } } //运用 $leasons[] = new Seminar(new Timed_Const_Strategy(4)); $leasons[] = new Lecture(new Fixed_Const_Strategy(null)); foreach ($leasons as $leason){ print "leason charge : {$leason->const()}"; print "charge_type : {$leason->charge_type()}" } leason charge 20. charge_type : 多次课结算; leason charge 30. charge_type : 一次课结算;
相続は父と子の関係です。
3 オブジェクトの生成
3.1 シングルトンパターン
重要なポイント
1: コンストラクターはプライベートです。
2: クラス自体には、独自のインスタンス化プロパティが含まれています。
class Preferences { private static $instance; private function __construct(){ ... } public static function get_instance(){ if(empty(self::$instance)){ self::$instance = new Preferences(); } return self::$instance; } ... } //使用 $preferences = Preferences::get_instance();
1 つの親クラスを使用して、異なる機能を持つ複数のサブクラスを生成します。
特徴:商品側(新浪微博)と需要側(新浪微博表示)の1対1対応
問題: Evernote では、ソースが Sina Weibo または開発者 Toutiao である可能性があります。Evernote が表示されると、2 つのヘッダーとフッターが異なります。
3.3 抽象パターン
RLGL!!!。Evernote は Sina Weibo のコンテンツを表示するだけでなく、私の Sina アカウントと Weibo も表示する必要があります。心配しないでください。
ファクトリーモードは主に、1対1に対応する製品側と需要側を生産するために使用されますが、抽象モードは1つの需要側(Evernote_display Sina Weibo)と複数の工場(需要側を複数の需要者に抽象化)を持ちます。 )、Sina コンテンツを提供する工場、Sina アカウントを提供する工場、Weibo コンテンツにコメントを提供する工場など。
コード:
abstract class Show_Evernote { abstract function get_header_text(); abstract function get_context(); abstract function get_footer_text(); abstract function get_user(); abstract function get_comment(); } class 显示新浪微博 extends Show_Evernote{ function get_header_text(){...}; function get_context(){new 新浪微博_内容;} function get_footer_text(){...}; function get_user(){new 新浪微博_账号 ;} function get_comment(){new 新浪微博_评论;} } //使用 印象笔记控件类->内容 = 显示新浪微博->get_context; 印象笔记控件类->账号 = 显示新浪微博->get_context; ...
3.4 平行模式
当使用工厂/深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル必须要制定具体的创建者(需求方).
平行模式和深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル的模型图一致,但代码实现不一样.
深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル中父类均为抽象类,而平行模式中,所以类都为普通类,方便父类的实例化.
在这里列出显示印象笔记类的实现代码
class Show_Evernote{ private $内容; private $账号; private $评论; function __construct(内容,账号,评论){ $this->内容 = 内容; $this->账号 = 账号; $this->评论 = 评论; } function get_内容(){ return clone $this->内容); } function get_账号(){ return clone $this->账号); } function get_评论(){ return clone $this->评论; } } //使用 $factory = new Show_Evernote( new 新浪微博内容(), new 新浪微博账号(), new 新浪微博评论() ); 印象笔记控件类->显示印象笔记 = $factory;
其实大家可以发现,原型模式只不过只在最顶层类中包装了一下各组件子类而已,然而这样可以轻松的组合他们,例如实现一个显示新浪微博内容,但要显示开发者头条账号的需求?
4 使用对象
4.1 深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル
深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル,可以理解为单一对象管理组合对象(聚合组件),最终组合体下的各个组合部件最好类型一致.不然特殊性越多,需要判断就越多.
假设捶背男,洗脚男,洗发男,用来服务一个人(妹子).
假设妹子的几个部位可用的服务男均为无限个.
//创建一个妹子 $妹子 = new 人(); //添加洗脚男、捶背男 $妹子->add_man(new 洗脚男); $妹子->add_man(new 捶背男); //循环所有男的给予舒服的方法. $妹子->计算舒服程度();
这是一个很理想的深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル,在现实情况,我们使用深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル,可能不得不创建多种类型的洗脚男,需要添加许多判断条件.
4.2 深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル
深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル,首先洗脚男,洗发男,捶背男都是人,但是如果,一个男的又捶背,又洗发,这怎么玩?.add_man两次?这不科学吧,来给这些男的装饰一下吧~
abstract class 人{ ... abstract function get_well(); } class 男 extends 人 { //无论你是神马男,服务你,你就能获得10点舒服度. private $well = 10; function get_well(){ return $this->well(); } } abstract class 装饰男类型 extends 人 { protected $人; function __construct(人 $人){ $this->人 = $人; } } class 捶背装饰 extends 类型男装饰{ function get_well(){ return $this->人->get_well()+30; } } class 洗发装饰 extends 类型男装饰{ function get_well(){ return $this->人->get_well()+20; } } class 洗褪装饰 extends 类型男装饰{ //老子不喜欢别人碰我的毛裤. function get_well(){ return $this->人->get_well()-20; } } //创建捶背,能给予的舒服指数 - -嘻嘻. $人 = new 捶背装饰(new 男); $人->get_well(); // 10+30 = 40 //来来来,全能选手,捶背、洗发、洗腿一起来 $人 = new 洗脚装饰(new 洗发装饰(new 捶背装饰(new 男()))); //10+30+20-20 = 40,注意顺序,由里到外执行.
深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル,既(组合+继承),基类方法一定要尽量少,不然子类可能有它不该有的方法.直接类继承,她只可能是一种形态,而她的多种形态可能一并拥有的时候,应该运用组合.
继承即单一多态,组合既多种多态.
这个例子中,你可以添加女,然后把装饰男类型改为装饰通用类型,但每个get_well()都要多一个判断是男还是女(如果给予的舒服程度不一样).
这只是确保不可能出现在男,女之外的第三种人,如果基类为动物,给予服务的可能是鸡,鹅,鸭,那么装饰类型应该运用深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル,动物形态和装饰形态一一对应.方便拓展.
除了服务类型,服务男的样子也很重要,这就多了一种装饰,现在有装饰男类型和相貌男类型,这种情况怎么破,其实类似.
4.3 外观模式
即给外部系统提供清晰接口
例如当Model层写得很混乱,但是里面的方法还能用,那我们的Controller层应该列举一些清晰的访问方法来供View层访问.外观模式,强调的是清晰的访问接口.
5 执行任务
5.1 策略模式
给类添加功能.对象要显式的调用它.
继续刚才的洗脚男和人的故事吧...你丫的爽完了要给钱吧?支付宝?微信?现金?
这个付款方式有多种,实现方法不应该放在人类中,而是应该委托给别的类
abstract class 人 { protectd $支付方式; function set_支付方式(){...} function 付款(金额){ return $支付方式->付款($金额); } } abstract class 付款{ abstract function 付款($金额); } class 支付宝付款 extends 付款{ function 付款($金额){ return 外接支付宝付款流程($金额); } } ... //使用 $男 =new 男(); ///爽爽爽 ... //结账 $支付宝支付账单 = new 支付宝付款($金额); $人 = new 男(); $人->set_支付方式(new 支付宝付款()); $人->付款();
5.2 深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル
当被观察者发生变化,观察者需要被通知.
当数据发生变化,页面需要被通知.
使用步骤:
观察者加载到被观察者中.
被观察者通知观察者.
例如登陆类(被观察)状态改变,要出发邮件系统和日志系统(观察者)
interface 被观察者{ function attach(观察者); function detatch(观察者); function notify(); } class Login implements 被观察者{ private $观察者; function __construct(){ $this->观察者 = array(); } function attach($观察者){ $this->观察者 = $观察者; } function detach($观察者){ //删除某个观察者的操作; } function notify(){ foreach ($this->观察者 as $单个观察者){ $单个观察者->update($this); } } } interface 观察者{ function update(被观察者); } abstract class Login_观察者 implements 观察者{ private $login; function __construct (Login $login){ $this->login = $login; $login->attach($this); } function update(观察者 $观察者){ if ($观察者 ===$this->login){ $this->do_update($观察者); } } abstract function do_update(Login $login); } class 邮件观察者 extends 登陆观察者 { function do_update(Login $login){ //判断条件 发送邮件 } } class 日志观察者 extends 登陆观察者 { function do_update(Login $login){ //判断条件 记录到日志; } } //使用 $login = new Login(); new 邮件观察者 ($login); new 日志观察者 ($login);
PHP有内置的SPL实现上述的深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル.
5.3 深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル
问题: 在一个军队中,有很多军队,军队下面可能包含军队/步兵/弓箭手,这时我们要显示一个军队的战斗力/需要粮食的各级分配?(遍历对象并设置显示方法).怎么办?.解决办法是军队还是保存自己的基本信息,设置一个访问者,访问者包含总战斗力方法和总粮食的方法.
访问者
abstract class 军队访问者{ abstract function 访问(单元); function 访问军队($军队){ $this->访问($军队); } function 访问弓箭手($弓箭手){ $this->访问($弓箭手); } //这里重复定义了大量代码,其实可以用call来替代 function __call($method_name,$args){ if(strrpos($method_name, "访问")){ return call_user_func_array( array($this,"访问"),$args ); } } } class 军队战斗力访问者 extends 军队访问者{ private $text=""; function 访问($单元){ $ret = ""; $pad = 4*$单元->getDpth(); //设置显示深一级前面多4个空格. $ret .= sprintf( "%{$pad}s",""); $ret .= get_class($单元). ": "; $ret .= "战斗力: " .$单元->bombardStrenth()."\n"; $this->text .=$ret; } function get_text(){ return $this->text; } }
被访问者
abstract class 单元{ function 接受($军队访问者){ $method = "访问_".get_class($this); $军队访问者->$method($this); } private $depth; protected function set_depath($depth){ $this->depth=$depth; } function get_depth(){ return $this->depth; } ... } abstract class 综合单元 extends 单元{ function 接受($军队访问者){ parent::接受($军队访问者) foreach($this->单元集合 as $this_unit){ $this->unit->接受($军队访问者); } } } class 军队 extends 综合单元{ function bombardStrenth(){ $ret =0; foreach($this-units() as $unit){ $ret += $unit->bombardStrenth(); } return $ret } } class 弓箭手 extends 单元{ function bombardStrenth(){ return 4; } }
调用
$main_army = new Army(); $main_army->add_unit(new 步兵()); $main_army->add_unit(new 弓箭手()); $军队战斗力访问者_实例 =new 军队战斗力访问者(); $main_army->接受(均分战斗力访问者); print $军队战斗力访问者->get_text();
输出
5.4 深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル
例子为Web页面的login和feed_back,假如都需要使用ajax提交,那么问题来了,将表单封装好提交上去,得到了返回结果.如何根据返回结果跳转不同的页面?.
有些同学就说了,login和feed_back各自写一个方法憋,提交的时候调用各自的方法.
然后再来个logout命令..增加..删除..命令怎么办..
深い PHP オブジェクト指向、パターンと実践 - JavaScript スキル比较适合命令执行例如登陆,反馈等简单只需要判断是否成功的任务
命令:
abstract class Command{ abstract function execute(Conmmand_Context $context); } class Login_Command extends Command{ function execute(CommandContext $context){ $managr =Register::getAccessManager(); $user = $context->get("username"); $pass = $context->get('pass'); $user_obj = $manager->login($user,$pass); if(is_null($user_obj)){ $context->setError($manager->getError()); return false; } $context->addParam("user",$user_obj); return true; } }
部署命令的调用者
class Command_Facotry{ public function get_command($action){ $class = UCFirst(strtolower($action))."_Command"; $cmd = new $class(); return $cmd; } }
客户端
class Controller{ private $context; function __construct(){ //Command_Context主要用来存储request和params $this->context =new Command_Context(); } function process(){ $cmd Command_Factory::get_commad($this->context->get('action')); if(!$cmd-execute($this->context)){ //错误处理 }else{ //成功 分发视图 } } }
使用
$controller =new Controller(); $context = $controller->get_context(); $context->add_param('action','login'); $context->add_param('username','404_k'); $context->add_param('pass','123456'); $controller->process();