NoteDeep

类,对象,方法和属性

面向对象编程是一种编程风格,习惯上把一个特定主题的所有变量和功能归为一个类。面向对象程序设计被认为比过程式的程序风格更加先进和高效。这个效率源于它支持更好的代码组织,提供模块化,并减少了重复自己的代码。据说,我们可能还是喜欢在小而简单的项目中使用过程式的风格。然而,随着我们的项目越来越复杂,我们最好使用面向对象的风格。

我们将讨论这几个问题
如何创建类?
如何添加属性到一个类?
如何从一个类创建对象?
如何获取和设置对象的属性?
如何添加方法到一个类?

如何创建类?

对于下面给出的例子,我们将创建一个汽车类。
class Car {
// The code
}

如何添加属性到一个类?

让我们添加一些属性到汽车类。属性可以像任何其他变量一样接受字符串,整数和布尔值(真/假值)等值。
class Car {   
public $comp;   
public $color = 'beige';   
public $hasSunRoof = true;
}

如何从一个类创建对象?

我们可以从同一个类创建多个对象,每个对象都有自己的一组属性。我们使用new关键字创建对象。创建对象的过程也称为实例化。
$bmw = new Car();

对象,它们有什么好处?
在过程式的风格中,所有的函数和变量在全局范围内,通过调用它们的名字来访问它们。
而类的存在,使类内部的任何东西都隐藏起来。这是因为类中的代码被封装在类范围内,而不在全局范围内。通过从类中创建对象来实现这一点。
我们可以从同一个类创建尽可能多的对象,他们都将共享类的方法和属性。看到下面的图片:


从相同的汽车类,我们创造了三个单独的对象变量:奔驰,宝马,和奥迪。
尽管所有的对象都是由同一个类创建的,因此具有类的方法和属性,但它们仍然不同。这不仅仅是因为它们具有不同的变量名,而且因为它们可能具有分配给它们属性的不同值。

例如,在上面的图像中,它们的颜色属性不同 - 奔驰是绿色,宝马蓝色,奥迪是橙色。

如何获取/设置对象的属性?

一旦我们创建了一个对象,我们就可以得到它的属性。

class Car {   
public $comp;   
public $color = 'beige';   
public $hasSunRoof = true;
}
$bmw = new Car();
echo $bmw->color; //获取属性 beige
$bmw->color = 'blue'; //设置属性
echo $bmw->color; //获取属性 blue

如何添加方法到一个类?

类通常包含函数。一个类中的函数被称为方法。这里我们将方法hello() 添加到类中。
class Car {
public $comp;
public $color = 'beige';
public $hasSunRoof = true;
public function hello()
{
return "beep";
}
}
$bmw = new Car ();
$mercedes = new Car ();  
echo $bmw -> hello(); // beep
echo $mercedes -> hello(); // beep


$this关键字

$this表示该对象自己,并允许我们在类的范围内访问它。
class Car { // The properties
public $comp;     
public $color = 'beige';     
public $hasSunRoof = true;       
// 这个方法现在可以获取到类内的属性了。
public function hello(){       
return "Beep I am a <i>" . $this->comp . "</i>, and I am <i>" . $this->color;     
}
}
$bmw = new Car();
$mercedes = new Car ();
$bmw->comp = "BMW";
$bmw->color = "blue";
$mercedes->comp = "Mercedes Benz";
$mercedes->color = "green";
//我们调用hello方法。
echo $bmw->hello(); // Beep I am a BMW, and I am blue.
echo $mercedes->hello(); // Beep I am a Mercedes Benz, and I am green.

链式方法和属性

class Car {     
public $tank;  //剩余的油量   
//加油
public function fill($float) {     
$this-> tank += $float;       
return $this;   
}      
// 汽车行驶并且消耗油
public function ride($float)  {     
$miles = $float;     
$gallons = $miles/50;     
$this->tank -= ($gallons);       
return $this;   
}
}
$bmw = new Car();  
//加10份油,并且行驶40miles,得到最后的剩余油量$tank.
$tank = $bmw->fill(10)->ride(40)->tank;  
echo $tank;
//result 9.2



public修饰符允许来自外部或内部的代码访问类的方法和属性。
private修饰符则阻止从类外的任何代码访问类的方法或属性,当我们尝试从类的外部设置它的值时,我们遇到了一个致命的错误。

class Car {
private $model;
public function getModel()
{
return "The car model is " . $this -> model;
}
}
$mercedes = new Car();
//无法从类的外部访问或者设置model私有属性的值。故会报错
$mercedes->model="Mercedes benz";
echo $mercedes->getModel();



如何设置和读取 私有属性呢?

为了与私有属性进行交互,我们使用public方法,因为它们可以与类外和类内的代码交互。
如下:
<?php
class Car {
private $model;
//public的方法允许被类外部代码访问
public function setModel($model)
{
$this->model = $model;
}
public function getModel()
{
return "The car model is " . $this->model;
}
}
$mercedes = new Car();
$mercedes -> setModel("Mercedes benz");
echo $mercedes -> getModel();
?>


__construct()构造函数

魔术方法的名字总是以两个下划线开头,而__construct()方法也不例外。当我们实例化(new)一个对象的时候,就会自动调用__construct构造函数。这种方法称为构造函数。
通常我们使用构造函数初始化一个对象,比如为一个属性设置一个值。

class Car{
private $model;
// A constructor method.
public function __construct($model)
{
$this -> model = $model;
}
}
$car1 = new Car ('mercedes');

__CLASS__

魔术常量,用于获取类名。
class Car {
private $model = '';
//__construct
public function __construct($model = null)
{
if($model)
{
$this -> model = $model;
}
}
public function getCarModel()
{
return " The <b>" . __CLASS__ . "</b> model is: " . $this -> model;
}
}
$car1 = new Car('Mercedes');
echo $car1 -> getCarModel();
// The
// Car
// model is: Mercedes

其他魔术常量:

__LINE__
得到使用该常数的行号
__FILE__
获取使用该常量的完整路径或文件名。
__METHOD__
获取使用该常量的方法的名称


当程序员不止一次地写同一个代码时,就会出现代码重复。在继承中,我们有一个拥有自己的方法和属性的父类,以及可以使用父代码的子类(或多个类)。通过使用继承,我们可以创建一个可重用的代码片段,只在父类中写入一次,并在子类中再次使用。

为了声明一个类继承另一个类的代码,我们使用extends关键字:

class Parent {
// The parent’s class code
}
class Child extends Parent {
// The child can use the parent's class code
}

在下面的例子中,跑车类继承了汽车类,所以它可以访问汽车的所有方法和非私有的属性。

//父类
class Car {
private $model;
//Public setter method
public function setModel($model)
{
$this -> model = $model;
}
public function hello()
{
return "beep! I am a <i>" . $this -> model . "</i><br />";
}
}
//子类
class SportsCar extends Car {
}
//实例化一个子类
$sportsCar1 = new SportsCar();
//使用父类的方法
$sportsCar1 -> setModel('Mercedes Benz');
echo $sportsCar1 -> hello();

子类重写父类的方法
// The parent class has hello method that returns "beep".
class Car {
public function hello()
{
return "beep";
}
}
//The child class has hello method that returns "Halllo"
class SportsCar extends Car {
public function hello()
{
return "Hallo";
}
}
$sportsCar1 = new SportsCar();
echo $sportsCar1 -> hello(); //Hallo

使用final关键字禁止子类重写父类的方法

// The parent class has hello method that returns "beep".
class Car {
final public function hello()
{
return "beep";
}
}
class SportsCar extends Car {
public function hello()
{
return "Hallo";
}
}
$sportsCar1 = new SportsCar();
echo $sportsCar1 -> hello();


抽象类和抽象方法

抽象类是至少有一个抽象方法的类。抽象方法只能有名字和参数,没有其他的代码。因此,我们不能从抽象类中创建对象。
我们使用抽象类,但是我们只能确定方法的名字,而不是写这个方法的细节。
我们需要创建子类,将代码添加到子类方法体内的,最后使用这些子类创建对象。

抽象类用abstract关键字声明,并且包含抽象方法。
abstract class Car {
abstract public function calcNumMilesOnFullTank();
}


抽象类可以有非抽象方法。其实它甚至可以有属性。
abstract class Car {
protected $tankVolume;
public function setTankVolume($volume)
{
$this -> tankVolume = $volume;
}
// Abstract method
abstract public function calcNumMilesOnFullTank();
}

class Honda extends Car {
//因为我们继承了抽象类,所以我们需要在子类中定义它的抽象方法,
//并且向方法的主体添加代码
public function calcNumMilesOnFullTank()
{
$miles = $this -> tankVolume*30;
return $miles;
}
}

interface接口 - 类似于抽象类。
定义好一个接口,那么和团队中其他工程师合作的时候,它们如果实现了你的接口,就必须在类里实现你在接口里定义好的方法。
interface Car {
public function setModel($name);
public function getModel();
}

class miniCar implements Car {
private $model;
public function setModel($name)
{
$this -> model = $name;
}
public function getModel()
{
return $this -> model;
}
}

一个类可以同时实现多个接口
interface Vehicle {
public function setHasWheels($bool);
public function getHasWheels();
}

class miniCar implements Car, Vehicle {
private $model;
private $hasWheels;
public function setModel($name)
{
$this -> model = $name;
}
public function getModel()
{
return $this -> model;
}
public function setHasWheels($bool)
{
$this -> hasWheels = $bool;
}
public function getHasWheels()
{
return ($this -> hasWheels)? "has wheels" : "no wheels";
}
}

抽象类和接口有什么区别?

我们看到抽象类和接口是相似的,因为它们提供了必须在子类中实现的抽象方法。但是,他们仍然有以下不同:


interface
abstract class
代码
  • 抽象方法(空架子)
  • 常量
  • 抽象方法
  • 常量
  • 具体的方法(有具体实现的方法)
  • 具体变量
可见性
  • 只能是public
  • public
  • protected
  • private


同一个类可以同时实现多个接口
同一个类只能继承一个抽象类



php5不允许对基本数据类型(整数,浮点数,字符串和布尔值)进行参数预检

下面的代码仅仅在php7才是有效的
class car {
protected $model;
protected $hasSunRoof;
protected $numberOfDoors;
protected $price;
// string type hinting
public function setModel(string $model)
{
$this->model = $model;
}
// boolean type hinting
public function setHasSunRoof(bool $value)
{
$this->hasSunRoof = $value;
}
// integer type hinting
public function setNumberOfDoors(int $value)
{
$this->numberOfDoors = $value;
}
// float type hinting
public function setPrice(float $value)
{
$this->price = $value;
}
}

而php5和7都支持,对类型的参数进行预检。
如下:
class Car {
protected $driver;
// The constructor can only get Driver objects as arguments.
public function __construct(Driver $driver)
{
$this -> driver = $driver;
}
}
class Driver {}
$driver1 = new Driver();
$car1 = new Car ($driver1);

类定义静态方法

有些时候,我们需要使用属性或者方法,但是没有必要创建一个对象。这个时候,就可以为类定义静态方法。
常见的使用场景: 可以使用静态变量和静态方法来计数。

class Utilis {
// 通过static关键字定义静态属性
static public $numCars = 0;
// 定义静态方法
static public function addToNumCars($int)
{
$int = (int)$int;
self::$numCars += $int;
}
}
// 设置静态属性
Utilis::$numCars = 3;

// 获取静态属性
echo Utilis::$numCars; // 3

// 调用静态方法
Utilis::addToNumCars(3);
echo Utilis::$numCars; // 6


使用场景2:可以使用静态方法来构建一个工具类

class Utilis {
// 使用php的header方法来让用户跳转到相应的url
static public function redirect($url)
{
header("Location: $url");
exit;
}
}
Utilis::redirect("http://www.notedeep.com");

trait

http://php.net/manual/zh/language.oop5.traits.php

可以让两个无关的类具有类似的行为。

自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait。
Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。




评论列表

    类,对象,方法和属性
    如何创建类?
    如何添加属性到一个类?
    如何从一个类创建对象?
    如何获取/设置对象的属性?
    如何添加方法到一个类?
    $this关键字
    链式方法和属性
    如何设置和读取 私有属性呢?
    __construct()构造函数
    __CLASS__
    其他魔术常量:
    抽象类和抽象方法
    抽象类和接口有什么区别?
    类定义静态方法
    trait