Learning PHP —— 设计模式 | Chap1:浅谈设计模式中的OOP

468 查看

为什么使用OOP

  • OOP是一个模块化的过程,目的是为了把复杂问题简单化,一个模块解决一个复杂问题的某一个方面,即一个类应当只有一个职责

  • OOP区别于顺序式编程与过程式编程,在于:

    1.顺序编程:用于处理最简单的问题
    
    2.过程编程:将代码分为模块以便在程序中重用,程序猿输为一个函数
    输入不同实参就能利用不同具体值使用函数
    
    3.OOP:形同过程编程,长处在于能处理自己的数据结构,更方便地处理
      独立但相互关联的类
  • 从外部源启动PHP类,往往由其它模块调动,不会自启动,举个反例:

<?php 
    class TellAll{
        private function __construct(){
            ...
        }
    }
    $tellAll = new TellAll();
?>

OOP基本概念

抽象(abstract)

  • 用于指示一个对象的基本特征,是处理复杂性的具体方法。
    我们会对现实中的相似性分组,对具体的相似性抽象

举个例子:我们一般不会叫自己的爱宠哈士奇为"我的蠢萌蠢萌的、摇着尾巴的、爱捣乱的名叫Jeff的哈士奇",而往往会直接说:"我的狗!"

  • 抽象类不能实例化,只能由具体类(可以实例化的类,非抽象类)继承抽象类的接口以及所有具体属性

  • 所有类都是对数据的一组操作的抽象

  • 基本语法:

<?php 
    //IAbstract.php
    abstract class IAbstract{
        public $storeHere;    //可以定义属性
        public function test(){
            ...//非抽象类,继承子类无须实现
        }
        abstract public function trick($whatever);
        //作为对继承子类的限制
    }
?>
<?php
    //OneAbstract.php
    class OneAbstract extends IAbstract{
        public function trick(){
            ...//声明了抽象方法就必须实现
        }
    }
?>

接口(interface)

  • 不能像抽象类那样在接口中包含具体方法或变量,除非是具有抽象性的接口,可以包含具体常量

  • 接口的核心部分由类中操作(函数)定义的所有签名组成。签名包括一个操作的操作名和参数,一个接口表示的就是所有签名的集合。例如以下代码,签名由trick()和形参$whatever组成

public function trick($whatever){
    echo 'signature';
}
  • 如果要实现接口(implements),就要保证实现接口中的所有方法,在此基础上可以再继续添加额外的方法

<?php
    //ImethodHolder.php
    interface IMethodHolder{
        public function getInfo($info);
        public function sendInfo($Info);
        public function calculate($first,$second);
    }
?>
<?php
    //ImplementAlpha.php
    include_once('IMethodHolder.php');
    class ImplementAlpha implements IMethodHolder{
    
        /********  接口中所有方法必须实现   ********/
        public function getInfo($info){
            echo 'This is my News!' . $info."<br/>";
        }
        public function sendInfo(){
            return $Info;
        }
        public function calculate($first,$second){
            $calculate = $first * $second;
            return $calculate;
        }
        
        
         /********  除此之外可以增加其他类   ********/
        public function useMethods(){
            $this->getInfo('习近平会见马英九');
            echo $this->sendInfo('恒大战平阿赫利') . "<br/>";
            echo "Salary : $" . $this->calculate(20,15) ;
        }
        
        $worker = new ImplementAlpha;
        $worker->useMethods();
        //前面提到不要自启动,这里为了方便就直接自启动实例化类
        //不过可以看到,在实现类中,方法都是经过useMethods的方法调用的
        //在后面章你会看到很多例子都是这样实现的,而往往不会直接调用
    }
?>
  • OOP和设计模式有很多重要的结构要素,其中之一就是指定数据类型为接口而不是一个具体实现,对数据的引用要通过父类完成。如下所示:

<?php
    //IProduct.php
    interface IProduct{
        function apples();
        function books();
    }
?>
<?php
    //FruitStore.php
    include_once('IProduct.php');
    class FruitStore store implements IProduct{
        public function apples(){
           return  'Fruit shop-----We have apples';
        }
        public function books(){
             return  'Fruit shop-----We don't have books';
        }
    }
?>

<?php
    //BookStore.php
    //再来一个BookStore类同上,No apples,but books
?>
<?
//useProduct.php
    //两个实现include进来,此处省略代码
    class useProduct{
        public function __construct(){
            $apple = new FruitStore();
            $book = new BookStore();
            $this->doInterface($apple);
            $this->doInterface($book);
            //实例化类在一个方法中,调用类的方法又在另一个方法中,降低了耦合度
        }
        function doInterface(IProduct $product){
            echo $product->apples();
            echo $product->books();
        }
    }
    $worker = new useProduct();
?>

这就是强制数据类型,其中使用的对象(类)必然有给定的接口,如果把一个接口 (一个对象类或者接口) 作为代码提示,绑定会更宽松)它会绑定到接口而不是绑定到一个特定的实现,如上面的doInterface(IProduct $product)

封装(Encapsulation)

  • 封装是画份一个抽象的诸多元素的过程,这些元素构成该抽象的结构和行为,封装的作用就是将抽象的契约接口与其实现分离

  • 通过3种类型的可见性保护封装(private/protected/public)。但是一个类还是要有可见方法访问封装,比如__consruct构造函数,默认为公共的

  • 为了保持封装而又提供可访问性,OOP建议使用getter()和setter()方法,但是不能滥用,否则会破坏封装

<?php
class GetSet{
    private $data;
    function __construct(){
        $this->setter(200);
        $got = $this->getter();
        echo $got;
    }
    private function getter(){
        return $this->data;
    }
    private function setter($setValue){
        $this->data = $setValue;
    }
}
$worker = new GetSet();
?>

继承(extends)

  • 假设我有一只哈士奇(我们称之为子类),那么它继承了狗(父类)的属性,比如四只脚,看到的世界都是通过#404040层过滤的。而哈士奇区别于普通狗,又有新的特征:逗比,爱捣乱

  • 为了保证类之间的松绑定,通常会继承抽象类,而且是浅继承(只有一层子类)。深继承的话,一旦对父类进行简单修改,子类会带来严重的破坏

<?php
//dog.php
    class dog{
        protected $pet;
        //共同特征,四条腿
        protected function fourlegs(){
            return 'walk on all four';
        }
        
        //宠物性格
        protected function character($petChar){
            $this->$pet = $petChar;
        }
    }
?>
<?php
//haski.php
include_once 'dog.php';
class haski extends dog{
    function __construct(){
        echo 'Haski ' . $this->fourlegs() . "<br/>";
        echo $this->character('They are crazy!') . "<br/>";
        echo $this->naughty() . "<br/>"; 
    }
    private function naughty(){
        return 'Haski dogs are naughty';
    }
}
?>
<?php
//client.php
    include_once 'haski.php';
    class client{
        function __construct(){
            $haski = new haski();
        }
    }
    $worker = new Client();
?>

多态(polymorphism)

  • 多态的真正价值在于可以调用有相同接口的对象来完成不同的工作。一个大型项目中,增加和修改会对程序带来巨大影响,除非有一个公共的接口(父类或接口),例如:

<?php
//Poly.php
interface ISpeed{
    function fast();
    function cruise();
    function slow();
}
class Jet implements ISpeed{
    function slow(){
        return 120;
    }
    function cruise(){
        return 1200;
    }
    function fase(){
        return 1500;
    }
}
class car implements ISpeed{
   function slow(){
        $carslow = 15;
        return $carslow;
    }
    function cruise(){
        $carcruise = 65;
        return $carcruise;
    }
    function fase(){
        $carZoom = 110;
        return $carZoom;
    }
}
$f22 = new Jet();
//各种调用,省略
$ford = new Car();
//同上
?>

从上面的例子看出,Jet和Car类对接口的实现有很大不同。但是基于这样一个公共接口,在一个给定的程序结构中做出修改或者增补时,可以放心地请求或使用接口方法,而不必担心程序会崩溃

抽象还是接口

(引用于CSDN-PHP的抽象类、接口的区别)

  • 如果要创建一个模型,这个模型将由一些紧密相关的对象采用,就可以使用抽象类。如果要创建将由一些不相关对象采用的功能,就使用接口。

  • 如果必须从多个来源继承行为,就使用接口。

  • 如果知道所有类都会共享一个公共的行为实现,就使用抽象类,并在其中实现该行为。


PS:该篇引用于《Learning PHP设计模式》第1、2章节

Chap2 预告 :设计模式与UML

  • 算法处理操作速度,设计模式解决开发速度