一.单例模式
目的:为了控制对象的数量(只能够有一个,相当于类的计划生育)
做法:
1.在类里面做了一个公有的静态方法来造对象
2.将类的构造方法做成私有的;还要防止子类中重新定义构造方法,所以这里还要加final; final private function __construct(){}
3.防止clone,所以我们还需要定义一个final private function __clone(){}
class Ren
{
public $name;
static public $dx; //静态成员变量用来存储该对象
final private function __construct() //把构造函数设置为私有,类的外部就不能用new造对象了
{
}
final private function __clone() //防止clone复制对象
{
}
static function DuiXiang() //做一个静态的方法,用来在类的内部造对象
{
if(empty(self::$dx)) //判断上面定义的变量是否有值,如果没有,就造一个对象,如果有,就直接输出这个
{
self::$dx = new Ren(); //因为是静态变量,只能用类名调用
}
return self::$dx;
}
}
$r = Ren::DuiXiang();
$r->name = "张三";
$r1 = Ren::DuiXiang();
$r1->name = "李四";
var_dump($r); //由于上面已经造了一个对象$r,所以当再造$r1时,$r1跟$r是一样的对象
二、工厂模式
工厂模式的最大优点在于创建对象上面,就是把创建对象的过程封装起来,这样随时可以产生一个新的对象。
减少代码进行复制粘帖,耦合关系重,牵一发动其他部分代码。
通俗的说,以前创建一个对象要使用new,现在把这个过程封装起来了。
假设不使用工厂模式:那么很多地方调用类a,代码就会这样子创建一个实例:new a(),假设某天需要把a类的名称修改,意味着很多调用的代码都要修改。
工厂模式的优点就在创建对象上。
工厂模式的优点就在创建对象上。建立一个工厂(一个函数或一个类方法)来制造新的对象,它的任务就是把对象的创建过程都封装起来,
创建对象不是使用new的形式了。而是定义一个方法,用于创建对象实例。
abstract class YunSuan
{
public $a;
public $b;
function Suan(){}
}
class Jia extends YunSuan
{
function Suan(){
return $this->a+$this->b;
}
}
class Jian extends YunSuan
{
function Suan(){
return $this->a-$this->b;
}
}
class Cheng extends YunSuan{
function Suan(){
return $this->a*$this->b;
}
}
//做一个工厂类
class GongChang{
static function ShengChan($fuhao){
if($fuhao=="+"){
return new Jia();
}else if($fuhao=="*"){
return new Cheng();
}
}
}
//算加法
$suan = GongChang::ShengChan("+");
$suan->a = 10;
$suan->b = 5;
echo $suan->Suan();
静态方法可以调静态变量,但不能调普通变量
普通方法可以调静态变量,也可以普通变量
面向对象三大基本特性与五大基本原则
三大特性是:封装、继承、多态
所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
封装是面向对象的特征之一,是对象和类概念的主要特性。 简单的说,一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。
所谓继承是指可以让某个类型的对象获得另一个类型的对象的属性的方法,它支持按级分类的概念。
继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。继承概念的实现方式有二类:实现继承与接口继承。实现继承是指直接使用基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
所谓多态就是指一个类实例的相同方法在不同情形有不同表现形式。
多态是面向对象的三大特性中除封装和继承之外的另一重要特性。它展现了动态绑定的功能,也称为“同名异式”。多态的功能可让软件在开发和维护时,达到充分的延伸性。事实上,多态最直接的定义是让具有继承关系的不同类对象,可以对相同名称的成员函数调用,产生不同反应效果。【多态性概念】所谓多态性就是指一段程序能够处理多种类型对象的能力。在PHP中,多态一般情况可以理解就是方法的重写【很多资料都是这么解释的,虽然这样不是很严禁!因为php不具有像java那种清晰的多态】。方法重写是指一个子类中可以重新修改父类中的某些方法,使其具有自己的特征。重写要求子类的方法和父类的方法名称相同,这可以通过声明抽象类或是接口来规范。
多态的好处:大大提高程序的扩展,增强系统的灵活性,降低模块间的耦合。
虽然php不具有像java那种清晰的多态;但是严禁上说可以通过编程逻辑实现多态,如下:php体现现了多态。
<?php
abstract class animal{
abstract function fun();
}
class cat extends animal{
function fun(){
echo "cat say miaomiao...";
}
}
class dog extends animal{
function fun(){
echo "dog say wangwang...";
}
}
function work($obj){
if($obj instanceof animal){
$obj -> fun();
}else{
echo "no function";
}
}
work(new dog());
work(new cat());
上面通过一个关键字instanceof来判断,变量指向的对象是否是animal类的一个实例,下面new cat(),new dog()都是animal子类的对象,而输出了“dog say wangwang…”和“cat say miaomiao…”,说明子类对象是父类的一个实例,从而达到了java多态的功能。
上边的类是抽象类,也表明了接口与实现接口的类对象同样可以适用。
至此,得出php虽然多态体现模糊,但还是具有多态特性的。
PHP5中实现多态的两种方法实例分享 https://www.jb51.net/article/49184.htm 浅谈多态以及php的实现方法 https://blog.csdn.net/whd526/article/details/70242918 重载不应归在多态的范畴内 https://blog.csdn.net/whd526/article/details/70240684 方法重写与方法重载的区别 https://www.cnblogs.com/zheting/p/7751787.html 重写: 类的继承关系可以产生一个子类,子类继承父类,它具备了父类所有的特征, 继承了父类所有的方法和变量。 子类可以定义新的特征,当子类需要修改父类的一些方法进行扩展,增大功能, 程序设计者常常把这样的一种操作方法称为重写,也叫称为覆写或覆盖。 重载: 方法重载是让类以统一的方式处理不同类型数据的一种手段。 调用方法时通过传递给它们的不同个数和类型的参数来决定具体使用哪个方法, 这就是多态性。 提示:因为php是弱类型语言,一个类中它也不允许定义相同方法名。 所以针对php,很多资料都把重写和重载看成一个概念【重写】。
五大基本原则
1、单一职责原则SRP(Single Responsibility Principle)
是指一个类的功能要单一,不能包罗万象。如同一个人一样,分配的工作不能太多,否则一天到晚虽然忙忙碌碌的,但效率却高不起来。
2、开放封闭原则OCP(Open-Close Principle)
一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。比如:一个网络模块,原来只服务端功能,而现在要加入客户端功能,
那么应当在不用修改服务端功能代码的前提下,就能够增加客户端功能的实现代码,这要求在设计之初,就应当将服务端和客户端分开,公共部分抽象出来。
3、替换原则(the Liskov Substitution Principle LSP)
子类应当可以替换父类并出现在父类能够出现的任何地方。比如:公司搞年度晚会,所有员工可以参加抽奖,那么不管是老员工还是新员工,
也不管是总部员工还是外派员工,都应当可以参加抽奖,否则这公司就不和谐了。
使用里氏替换原则时需要注意,子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能。
里氏替换原则是对类继承的一种约束。对里氏替换原则有两种理解:
1. 不能随便去继承不合适的,有多余方法或者属性的类。
2. 子类可以扩展父类的功能,但不能改变父类原有的功能。
4、依赖(倒置)原则(the Dependency Inversion Principle DIP) 具体依赖抽象,上层依赖下层。依赖倒置原则核心一句话:面向接口编程。
“依赖倒置是一种软件设计思想,在传统软件中,上层代码依赖于下层代码,当下层代码有所改动时,上层代码也要相应进行改动,因此维护成本较高。而依赖倒置原则的思想是,上层不应该依赖下层,应依赖接口。意为上层【汽车】代码定义接口,下层代码实现该接口,从而使得下层【具体汽车如宝马,奔驰】依赖于上层接口,降低耦合度,提高系统弹性”。
<?php
// 司机开奔驰,未用依赖倒置原则的写法
class Benz{
public function run(){
return " Benz is runing!!!";
}
}
class Driver{
public function drive(Benz $car){
echo $car -> run();
}
}
class Client{
public static function doing(){
$driver = new Driver();
$driver -> drive( new Benz() );
}
}
Client :: doing();
// 那么如果司机想开宝马呢?,是不是就要修改Driver了,这就违反了开闭原则了,怎么能只在Client添加代码就让宝马车也会开呢?
interface ICar{
//定义一个汽车接口
public function run();
}
class BMW implements ICar{
public function run(){
return "BMW is runing !!!";
}
}
class Benz implements ICar{
public function run(){
return "Benz is runing !!!";
}
}
interface IDriver{
//定义一个司机接口,以防以后有A照,B照,C照的
public function drive(ICar $car);
}
class Driver implements IDriver{
public function drive(ICar $car){
echo "<br>" . $car -> run();
}
}
class Client{
public static function doing(){
$driver = new Driver();
$driver -> drive( new BMW() ); //开宝马
$driver -> drive( new Benz() ); //开奔驰
.
.
.
.
}
}
Client :: doing();
?>
案例解释二:
比如有这么个需求,用户注册完成后要发送一封邮件,然后你有如下代码:
先有邮件类'Email.class.php'
class Mail{
public function send()
{
/*这里是如何发送邮件的代码*/
}
}
然后又注册的类'Register.class.php'
class Register{
private $_emailObj;
public function doRegister()
{
/*这里是如何注册*/
$this->_emailObj = new Mail();
$this->_emailObj->send();//发送邮件
}
}
然后开始注册:
include 'Mail.class.php';
include 'Register.class.php';
$reg = new Register();
$reg->doRegister();
看起来事情很简单,你很快把这个功能上线了,看起来相安无事... xxx天过后,产品人员说发送邮件的不好,要使用发送短信的,然后你说这简单我把'Mail'类改下...
又过了几天,产品人员说发送短信费用太高,还是改用邮件的好... 此时心中一万个草泥马奔腾而过...
这种事情,常常在产品狗身上发生,无可奈何花落去...
以上场景的问题在于,你每次不得不对'Mail'类进行修改,代码复用性很低,高层过度依赖于底层。那么我们就考虑'依赖倒置原则',让底层继承高层制定的接口,高层依赖于接口。
interface Mail
{
public function send();
}
class Email implements Mail()
{
public function send()
{
//发送Email
}
}
class SmsMail implements Mail()
{
public function send()
{
//发送短信
}
}
class Register
{
private $_mailObj;
public function __construct(Mail $mailObj)
{
$this->_mailObj = $mailObj;
}
public function doRegister()
{
/*这里是如何注册*/
$this->_mailObj->send();//发送信息
}
}
下面开始发送信息
/* 此处省略若干行 */
$reg = new Register();
$emailObj = new Email();
$smsObj = new SmsMail();
$reg->doRegister($emailObj);//使用email发送
$reg->doRegister($smsObj);//使用短信发送
/* 你甚至可以发完邮件再发短信 */
上面的代码解决了'Register'对信息发送类的依赖,使用构造函数注入的方法,使得它只依赖于发送短信的接口,只要实现其接口中的'send'方法,不管你怎么发送都可以。上例就使用了"注入"这个思想,就像注射器一样将一个类的实例注入到另一个类的实例中去,需要用什么就注入什么。当然"依赖倒置原则"也始终贯彻在里面。"注入"不仅可以通过构造函数注入,也可以通过属性注入,上面你可以可以通过一个"setter"来动态为"mailObj"这个属性赋值。
https://www.cnblogs.com/painsOnline/p/5138806.html
5、接口分离原则(the Interface Segregation Principle ISP)
模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来
关注公众号,了解更多it技术(it问答网)
写的太详细,太棒了,感谢徐老师