1.OOP思想与理论
其实我不知道在这里提OOP思想会不会是一种再炒蛋炒饭的行为,但是还是说说自己的理解。
OOP即Object-Oriented Programming,是面向对象程序设计的意思。
如果像我一样是学C出身的朋友,刚开始接触的时候可能觉得有点新奇;而没有泡在C这样面向过程的语言经验的朋友可能就觉得自然而然就这样子了——这应该就是传说中的可塑性强的意思了^_^。
OO(面向对象)的思想更接近于人类对自然界的认知——这话讲得似乎很高深,其实说白了就是人们如何理解和看待事物的。比如汽车是一个类(是抽象的,现实中不存在汽车这样的实物,存在的是汽车的各种实例),这个类的属性有:生产日期、轮子大小等;这个类的方法有:加速、减速、鸣喇叭等;而保时捷属于汽车这个类,是汽车这个类的一个实例——即对象,因此保时捷也具有这个类的属性和方法;当然,劳斯莱斯也有这些属性和方法,不过具体的细节显然和保时捷是不一样的。
(1)类和对象
人类是一个类,而你和我以及在看这篇文章的朋友是人类这个类的实例——即对象。我们就首先以人类为模型来学习,来看一段代码:
<?php
// @filename oop_1.php
// @author casual0402
// @contact casual0402@gmail.com
class People {
private $name;
function sayHello() {
echo "hello\n";
}
}
$man = new People ;
$man->sayHello() ;
?>
代码首先创建一个People类,该类有姓名这个属性,而且拥有会讲hello这样的方法;接着实例化一个对象$man,$man也一样有姓名这个属性和讲hello这个方法;最后调用sayHello()这个方法,使用的方式是:$man->sayHello()。
到这里,应该能很好地理解类和对象了吧^_^。
(2)继承、重载与多态
既然是人类的正常思维,那么继承就更好理解,无非就是基因的遗传嘛。
我们把中国人、美国人看做是继承人类的两个类。再来一段代码:
<?php
// @filename oop_2.php
// @author casual0402
// @contact casual0402@gmail.com
class People {
private $name;
function sayHello() {
echo "hello<br />";
}
}
class Chinese extends People {
function sayHello() {
echo "你好<br />";
}
}
class American extends People {
}
$cnMan = new Chinese;
$usaMan = new American;
$cnMan->sayHello();
$usaMan->sayHello();
?>
运行会发现输出为:
你好
hello
从这个实例中我们可以看出子类American继承了父类People的方法;当然,大家都是明白人,也知道子类Chinese也继承了父类People的属性和方法,但是中国人嘛,毕竟讲得最多的还是中国话;在Chinese类的定义中,重写父类中的属性或者方法叫做重载,我们完全可以理解成遗传变异或者叫做进化——恩,进化显得更好点。
而中国人和美国人讲"你好"的方式是不一样的,前者用汉语表现而后者用英语表现出来——这可以比较模糊地理解为多态,说白了,就是由于环境不同而产生了不同的进化方向。
(3)更明确地认识重载和多态
其实重载是多态的一种;如何表现多态呢——当然是重写方法嘛,这就是重载。引用上面的话:重载就是重写父类中的属性或者方法。而多态仍是继承的范围,只不过在遗传过程中表现出不同的进化方向。比如你和你弟弟都是遗传爸妈的,但是两个人一般是不一样的吧。因此多态就是同一个方法或者属性在不同的子类下表现出不同的形态。
(4)关键字this、作用域符号::、构造函数__construct()以及析构函数__destruct()
首先来看this。this关键字是指本体,即引用当前属性或方法的对象,比如$this->sayHello()表示引用自身的sayHello()方法,看下代码:
<?php
// @filename oop_3.php
// @author casual0402
// @contact casual0402@gmail.com
class People {
private $nationality = "noNationality";
private $name = "noName";
function sayHello() {
echo "hello<br />";
}
function setName($n) {
$this->name = $n;
}
function setNationality($n) {
$this->nationality = $n;
}
function writeName() {
echo $this->name . "<br />";
}
function writeNationality() {
echo $this->nationality . "<br/>";
}
}
class Chinese extends People {
function sayHello() {
echo "你好<br />";
}
}
class American extends People {
}
$cnMan = new Chinese;
$usaMan = new American;
$cnMan->setName("菜篮子");
$cnMan->setNationality("中国");
$cnMan->sayHello();
$cnMan->writeName();
$cnMan->writeNationality();
?>
输出为:
你好
菜篮子
中国
接着我们在American类中写上构造函数和析构函数,并用上作用域符号:
<?php
// @filename oop_4.php
// @author casual0402
// @contact casual0402@gmail.com
class People {
private $nationality = "noNationality";
private $name = "noName";
function sayHello() {
echo "hello<br />";
}
function setName($n) {
$this->name = $n;
}
function setNationality($n) {
$this->nationality = $n;
}
function writeName() {
echo $this->name . "<br />";
}
function writeNationality() {
echo $this->nationality . "<br/>";
}
}
class Chinese extends People {
function sayHello() {
echo "你好<br />";
}
}
class American extends People {
function sayHelloInChinese() {
echo "I can say hello in Chinese , you see , ";
Chinese::sayHello();
}
function __construct() {
echo "一个American类的实例被创建了<br />";
}
function __destruct() {
echo "一个American类的实例被撤销了<br />";
}
}
$cnMan = new Chinese;
$usaMan = new American;
$cnMan->setName("菜篮子");
$cnMan->setNationality("中国");
$cnMan->sayHello();
$cnMan->writeName();
$cnMan->writeNationality();
$usaMan->sayHelloInChinese();
?>
输出为:
一个American类的实例被创建了
你好
菜篮子
中国
I can say hello in Chinese , you see , 你好
一个American类的实例被撤销了
因此我们可以了解到__construct()函数在一个对象实例化的时候调用,而__destruct()函数在一个对象被撤销时调用——我们应该知道PHP5会自动回收资源,不必像C一样用上free();而作用域符号::指定调用哪个类中的属性或者方法。
(5)访问控制:public protected private
引用php.net的一段话:“对属性或方法的访问控制,是通过在前面添加关键字 public、protected 或 private 来实现的。由 public 所定义的类成员可以在任何地方被访问;由 protected 所定义的类成员则可以被其所在类的子类和父类访问(当然,该成员所在的类也可以访问);而由 private 定义的类成员则只能被其所在类访问。 ”
而且,方法如果没有指定关键字,默认为public。下见一段代码了解:
<?php
// @filename oop_5.php
// @author casual0402
// @contact casual0402@gmail.com
class People {
private $name;
protected $nationality;
public $sex;
function sayHello() {
echo "hello<br />";
}
}
$man = new People ;
$man->sayHello() ;
$man->sex = "男";
echo $man->sex;
$man->nationality = "中国";
echo $man->nationlity;
?>
输出为:
hello
男
Fatal error: Cannot access protected property People::$nationality ……
这说明了由 protected 所定义的类成员则可以被其所在类的子类和父类访问,但是不能被对象访问。当然,private限制得更多了,只能类本身访问。我们改进oop_5.php进一步了解访问控制:
<?php
// @filename oop_5.php
// @author casual0402
// @contact casual0402@gmail.com
class People {
private $name;
protected $nationality;
public $sex;
function sayHello() {
echo "hello<br />";
}
function setName($n) {
$this->name = $n;
}
function setNationality($n) {
$this->nationality = $n;
}
function writeName() {
echo $this->name . "<br />";
}
function writeNationality() {
echo $this->nationality . "<br />";
}
}
$man = new People ;
$man->sayHello() ;
$man->sex = "男";
echo $man->sex . "<br />";
class Chinese extends People {
protected $nationality = "中国";
function emigrate($n) {
$this->nationality = $n;
}
}
$cnMan = new Chinese;
$cnMan->setName("菜篮子");
$cnMan->setNationality("中国");
$cnMan->sayHello();
$cnMan->writeName();
$cnMan->writeNationality();
$cnMan->emigrate("美国");
$cnMan->writeNationality();
?>
输出为:
hello
男
hello
菜篮子
中国
美国
如果在最后加上一句:$cnMan->nationality = "菲律宾";则会出现错误:Fatal error: Cannot access protected property Chinese::$nationality in……
访问控制是为了实现信息的隐藏——即所谓的封装性。就好比你只要知道踩油门就可以加速而不必知道具体的工作原理,这就是封装了^_^。
(6)final static clone
final顾名思义就是最后的,指定某个方法不能重载或者某个类不能被继承。
static是指定静态方法或者变量的关键字。静态方法可以无需实例化类就可以使用,即用类名和作用域符号结合使用,而静态变量不止如此,它还在类的作用范围有“存储”功能,并且对象不能直接访问:
<?php
// @filename oop_6.php
// @author casual0402
// @contact casual0402@gmail.com
class test1 {
static $num1 = 1;
function addNum1() {
echo self::$num1 . "<br />";
self::$num1++;
}
}
class test2 extends test1 {
function writeParentNum() {
echo parent::$num1 . "<br />";
parent::$num1++;
}
}
echo test1::addNum1(); //输出1
$a = new test1;
$a->addNum1(); //输出2
$b = new test1;
echo $b->num1 . "<br />"; //不输出
echo test1::addNum1(); //输出3
test2::addNum1(); //输出4
$c = new test2;
$c->writeParentNum(); //输出5
echo 'the end';
?>
输出为:
1
2
3
4
5
the end
而第三个是clone,用法为$obj1 = clone $obj2;即复制$obj2所有属性和方法到$obj1——纯粹只是复制属性和方法而已,而二者的内存指向是不一样的,与$obj1 = $obj2是不同的,详细了解见下面代码:
<?php
class SubObject
{
static $instances = 0; //静态变量
public $instance;
public function __construct() { //构造函数
$this->instance = ++self::$instances;
}
public function __clone() { //当使用clone关键字时,会首先尝试寻找调用已定义的__clone()函数
//而且__clone()函数不能直接不调用
$this->instance = ++self::$instances;
}
}
class MyCloneable
{
public $object1;
public $object2;
function __clone()
{
// Force a copy of this->object, otherwise
// it will point to same object.
$this->object1 = clone $this->object1;
}
}
$obj = new MyCloneable();
$obj->object1 = new SubObject(); //这里会调用构造函数,静态变量增1,为1
//即$obj->object1的属性:$instance = 1; $instances = 1;
$obj->object2 = new SubObject(); //这里会调用构造函数,静态变量再增1,为2
//即$obj->object2的属性:$instance = 2; $instances = 2;
$obj2 = clone $obj; //这里会调用MyCloneable类中__clone()函数
//执行$this->object1 = clone $this->object1;(其中仅有$obj2->object1克隆了$obj->object1;)
//因此,$obj2->object1新开辟了一个内存空间,并且克隆了$obj->object1的所有属性
//可知 $obj2->object1->instance=1;$obj2->object1->instances=2;
//并且由于再度出现clone关键字,所以调用SubObject类中__clone()函数
//所以在这里执行了$this->instance = ++self::$instances;
//因此得到$obj2->object1->instance=3;
//而另外进行的是:$obj2->object2 = $obj->object2
//这样的环境我总是喜欢C语言中的解释:$obj2->object2 与 $obj->object2指向的是同一内存地址
//所以$obj2->object2 与 $obj->object2指向的是同一片内存空间,即二者的本质是一样的
//因此可知,$obj2->object2->instance=2;因为并没有使用clone关键字,所以没有调用__clone()函数
print("Original Object:<br />");
print_r($obj);
echo '<br />';
print("Cloned Object:<br />");
print_r($obj2);
echo '<br />';
?>
输出如下:
Original Object:
MyCloneable Object
(
[object1] => SubObject Object
(
[instance] => 1
)
[object2] => SubObject Object
(
[instance] => 2
)
)
Cloned Object:
MyCloneable Object
(
[object1] => SubObject Object
(
[instance] => 3
)
[object2] => SubObject Object
(
[instance] => 2
)
)
(7)抽象方法、抽象类以及接口
我根据php.net里Class Abstraction的内容直接翻译过来并摘要得:
1.抽象类不能被实例化,只能被继承再实例化。
2.只要含有抽象函数则该类必须为抽象类。
3.集成抽象类后,对于抽象函数的具体实现时要注意访问控制只能更松散而不能更严格,比如原来是protected关键字,实现时只能是protected或者public,而不能是private。
而关于接口,我也是直接根据php.net里的Object Interfaces的内容直接翻译:
1.接口允许用户指定一些某个类要实现的函数;
2.接口用interface关键字定义,但是接口内的函数不能有实际内容;
3.接口内所有函数必须指定为public,这是接口的本质;
4.类用implements关键字实现接口,如class Template implements iTemplate{}或者class Template implements iTemplate1,iTemplate2{};
5.实现多个接口的时候,各个接口内不能有重名属性或者含义,否则会有歧义;
6.一个类实现接口时必须具体化每个接口内指定的函数。
这样我们会发现当抽象类中所有函数都没有具体操作句柄的时候,就几乎为接口了^_^。
进一步学习请参考:
http://docs.php.net/manual/zh/language.oop5.abstract.php
http://docs.php.net/manual/zh/language.oop5.interfaces.php
(8)__autoload()
直接参考php.net的经典代码:
<?php
function __autoload($class_name) {
require_once $class_name . '.php';
}
$obj = new MyClass1();
$obj2 = new MyClass2();
?>
^_^自动加载函数__autoload()的作用是实例化一个对象时,如果当前文件内没有指定类则调用__autoload()。比如我们经常把某个类单独存为一个文件*.class.php,那么就可以用到了。
当然,OOP不仅仅是这些的,想要深入学习还得靠自己。 ^_^
接下来将会分享几个OOP实例,敬请期待,谢谢。
实例篇:
<?php
// @filename dbConnect.class.php
// @author casual0402
// @contact casual0402@gmail.com
class dbConnect {
private $dbhost;
private $dbuser;
private $dbpwd;
private $dbname;
public $link;
public $query;
function __construct($h,$u,$p,$n) {
$this->dbhost = $h;
$this->dbuser = $u;
$this->dbpwd = $p;
$this->dbname = $n;
}
function __destruct() {
mysql_close($this->link);
}
public function createCon() {
$this->link = mysql_connect($this->dbhost,$this->dbuser,$this->dbpwd);
mysql_select_db($this->dbname,$this->link);
mysql_query("set names utf8");
}
}
共同学习,写下你的评论
评论加载中...
作者其他优质文章