2012-05-28

Java 腦袋學 PHP OOP

PHP Object-oriented programming 在 5 以後的版本增強不少,對 Java 腦袋來說已經很接近了,尤其 OOP 用的 keyword 幾乎和 Java 的一樣,最大的闕漏在還沒有 package。
// 沒有 namespane,也就是 java 的 package
// 單一繼承,多重實做
class Book extends Publication implements Printable {
 
  // 三種修飾子:
  // private - 同一個 class
  // protected - 同一個 class 與子 class
  // public - 所有 class
  private $author;
  // 使用 static 定義 static property
  public static $TYPE = 'B';
 
  // 傳入參數可以指定型別,也可以不指定
  public function __construct($title, Author $author) {
    // 不像 java 會自動呼叫 parent 的 constructor,得手動呼叫,也就是說也可以不用呼叫
    // 呼叫 parent 的 properties 或者 methods 都得用兩個冒號,不是用「->」
    parent::__construct($title);
   
    $this->author = $author;
  }

 
  // 像是 Java finalize,當 PHP 執行結束或者物件被 unset 時,系統自動呼叫
  public function __destruct() { }
 
  // final 同 Java final,可以用在 class 或者 method 上
  public final function getBookTitle() {
    // 可以存取繼承來的 parent protected property
    return "[Book]$this->title";
  }

 
  public function getPublicationTitle() {
    // 使用兩個冒號呼叫 parent class 的 method
    return '[Publication]'.parent::getTitle();
  }

 
  // 使用 static 定義 static method
  public static function getType() {
    // 使用 parent 呼叫 parent class
    // 使用兩個冒號呼叫 static properties,不管是 parent 或自身的
    // 使用 self 呼叫自身的 static properties
    return parent::$TYPE.'/'.self::$TYPE;
  }

 
  public function getAuthor() {
    return $this->author;
  }

 
  // 因為 PHP 不支援 Java 的 Overloading,就是多個同名 method,但參數不同
  // 但是 PHP 支援不定型別參數,所以可以藉此實做 Overloading
  public static function printEverything($everything) {
    if (is_scalar($everything)) {
      if (is_string($everything)) {
        $var = '[string]'.$everything;
      }
      else if (is_int($everything)) {
        $var = '[int]'.(string) $everything;
      }
      else if (is_float($everything)) {
        $var = '[float]'.(string) $everything;
      }
      else if (is_bool($everything)) {
        $var = '[boolean]'.(string) $everything;
      }
      else {
        $var = '[unknown scalar]';
      }
    }
    else if (is_array($everything)) {
      $var = '[array]'.implode(',', $everything);
    }
    else if (is_object($everything)) {
        $var = '[Object]'.(string) $everything;
    }
    else {
      $var = '[unknown type]';
    }
    return $var;
  }

 
  // 一定要實做繼承來的 abstract method
  public function printOut() { }
 
  // 同 Java 的 toString()
  // 像 __construct 一樣,內建的 method 都是雙底線開頭
  public function __toString() {
    // 一定要回傳 string,不確定型別時,可以用(string)轉型
    return (string) $this->title;
  }

 
  // 若要做深層克隆,得實做這個 method
  public function __clone() {
    // 預設會傳一個淺層克隆物件進來,就是 $this
    // 只要再克隆必要的物件就可以達成深層克隆了
    // 只要有定義 __clone(),克隆是會無限遞迴呼叫下去的
    $this->author = clone $this->author;
  }

 
  // serialize() 時系統呼叫
  public function __sleep() {
    // 指定要 serialize 的屬性
    // 因為同一個物件可以被 serialize 多次,且 serialize 之後仍可使用
    // 所以__sleep() 裡不可做出影響物件狀態的動作
    // 僅用於排除不想 serialize 的屬性
    return array('title');
  }

 
  // unserialize() 時系統呼叫
  public function __wakeUp() {
    // 回復未被 serialize 的屬性,如資料庫連線或開啟的檔案之類的
  }
}


// abstract class
abstract class Publication {
 
  // child class 可以存取的 property
  protected $title;

  // 使用 static 定義 static property
  public static $TYPE = 'P';
 
  // constructor 使用固定的名字,注意是兩個底線,而且只能有一個 constructor
  public function __construct($title) {
    // 使用 $this 與「->」存取自身 properties 與 methods
    // 「$」用在 this 上,properties 不用再加,$this->$title 的「$title」是動態參數
    $this->title = $title;
  }

 
  public function setTitle($title) {
    $this->title = $title;
  }
 
  public function getTitle() {
    return $this->title;
  }

 
  // abstract method 不可以是 private 或者 final
  abstract function __toString();
}


interface Printable {
  function printOut();
}

class Author {
 
  private $name;
 
  public function getName() {
    return $this->name;
  }
 
  public function setName($name) {
    $this->name = $name;
  }
}


// 若無 constructor 或  constructor 不需參數,可以不用加小括弧
// classs name 不分大小寫
$author = new author;
$author->setName('Neil');
$book = new Book("Java Artisan", $author);


// 呼叫繼承來的 parent public method
print 'Title: '.$book->getTitle().'<br/>';

// 使用大括弧在雙引號裡使用複雜的變數整合
print "Book Title: {$book->getBookTitle()}<br/>";
print "Publication Title: {$book->getPublicationTitle()}<br/>";


// 使用兩個冒號呼叫 static method
print 'Type: '.Book::getType().'<br/>';

// 呼叫__toStringt()
print "Book: $book<br/>";

// 物件是 Pass By Reference
$b = $book;
$b->setTitle('Java Artisan in PHP');
print 'New Title: '.$book->getTitle().'<br/>';
print 'New Title: '.$b->getTitle().'<br/>';


// 克隆 - 預設是淺層克隆,物件 properties 是 copy by reference,物件以外的 properties 是 copy by value
// 物件以外指的是:字串、數值、日期、陣列、布林
$c = clone $book;
$c->getAuthor()->setName('Neil Chan');
print 'Old Name: '.$book->getAuthor()->getName().'<br/>';
print 'Clone Name: '.$c->getAuthor()->getName().'<br/>';


// 用 instanceof 檢查物件型別
print 'This is '.($book instanceof Book ? 'a book!' : 'not a book!').'<br/>';
print 'This is '.($book instanceof Publication ? 'a publication!' : 'not a publication!').'<br/>';
print 'This is '.($book instanceof Printable ? 'printable!' : 'not printable!').'<br/>';


// 用 class_implements 檢查實做 interfaces
$interfaces = class_implements('Book');
print 'This is '.(isset($interfaces['Printable']) ? 'printable!' : 'not printable!').'<br/>';
print 'The Book class implements '.implode(',', $interfaces).'.<br/>';


// 用 Reflection 檢查實做 interfaces
$rc = new ReflectionClass('Book');
print 'This is '.($rc->implementsInterface('Printable') ? 'printable!' : 'not printable!').'<br/>';


// 使用不定型別實做 Overloading
print 'String: '.Book::printEverything('string').'<br/>';
print 'Boolean: '.Book::printEverything(false).'<br/>';
print 'Integer: '.Book::printEverything(123).'<br/>';
print 'Float: '.Book::printEverything(12.3).'<br/>';
print 'Array: '.Book::printEverything(array('a','b')).'<br/>';
print 'Object: '.Book::printEverything($b).'<br/>';


// 看透整個物件
print 'var_dump: <br/>';
var_dump($book);
print '<br/>';

print 'var_export: <br/>';
var_export($book);
print '<br/>';

print 'print_r: <br/>';
print_r($book);
print '<br/>';

沒有留言:

張貼留言