观察者模式描述:
Laravel 的 Event 事件系统提供了一个简单的观察者模式实现,能够订阅和监听应用中发生的各种事件,在 PHP 的标准库 (SPL) 里甚至提供了三个接口 SplSubject, SplObserver, SplObjectStorage 来让开发者更容易地实现观察者模式,不过我还是想脱离 SPL 提供的接口和特定编程语言来说一下如何通过面向对象程序设计来实现观察者模式,示例是 PHP 代码不过用其他面向对象语言实现起来也是一样的。
模式定义:
1.观察者模式 (Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。
2.观察者模式又叫做发布 - 订阅(Publish/Subscribe)模式、模型 - 视图(Model/View)模式、源 - 监听器(Source/Listener)模式或从属者(Dependents)模式。
3.观察者模式的核心在于 Subject 和 Observer 接口,Subject (主题目标) 包含一个给定的状态,观察者 “订阅” 这个主题,将主题的当前状态通知观察者,每次给定状态改变时所有观察者都会得到通知。
4.发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。
模式结构说明:
Subject 目标抽象类
ConcreteSubject 具体目标
Observer 观察者抽象类
ConcreteObserver 具体观察者
应用举例:
比如在设置用户 (主题) 的状态后要分别发送当前的状态描述信息给用户的邮箱和手机,我们可以使用两个观察者订阅用户的状态,一旦设置状态后主题就会通知的订阅了状态改变的观察者,在两个观察者里面我们可以分别来实现发送邮件信息和短信信息的功能。
1.抽象目标类
abstract class Subject
{
protected $stateNow;
protected $observers = [];
public function attach(Observer $observer)
{
array_push($this->observers, $observer);
}
public function detach(Observer $ob)
{
$pos = 0;
foreach ($this->observers as $viewer) {
if ($viewer == $ob) {
array_splice($this->observers, $pos, 1);
}
$pos++;
}
}
public function notify()
{
foreach ($this->observers as $viewer) {
$viewer->update($this);
}
}
}
在抽象类中 attach detach 和 notify 都是具体方法,这些是继承才能使用的方法,将由 Subject 的子类使用。
2.具体目标类
class ConcreteSubject extends Subject
{
public function setState($state)
{
$this->stateNow = $state;
$this->notify();
}
public function getState()
{
return $this->stateNow;
}
}
3.抽象观察者
abstract class Observer
{
abstract public function update(Subject $subject);
}
在抽象观察者中,抽象方法 update 等待子类为它提供一个特定的实现。
4.具体观察者
class ConcreteObserverDT extends Observer
{
private $currentState;
public function update(Subject $subject)
{
$this->currentState = $subject->getState();
echo '<div style="font-size:10px;">'. $this->currentState .'</div>';
}
}
class ConcreteObserverPhone extends Observer
{
private $currentState;
public function update(Subject $subject)
{
$this->currentState = $subject->getState();
echo '<div style="font-size:20px;">'. $this->currentState .'</div>';
}
}
在例子中为了理解起来简单,我们只是根据不同的客户端设置了不同的内容样式,实际应用中可以真正的调用邮件和短信服务来发送信息。
5.使用观察者模式
class Client
{
public function __construct()
{
$sub = new ConcreteSubject();
$obDT = new ConcreteObserverDT();
$obPhone = new ConcreteObserverPhone();
$sub->attach($obDT);
$sub->attach($obPhone);
$sub->setState('Hello World');
$sub->notify();
}
}
$worker = new Client();
何时使用观察者模式:
1.一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
2.一个对象必须通知其他对象,而并不知道这些对象是谁。
3.基于事件触发机制来解耦复杂逻辑时,从整个逻辑的不同关键点抽象出不同的事件,主流程只需要关心最核心的逻辑并能正确地触发事件 (Subject),其余相关功能实现由观察者或者叫订阅者来完成。
总结:
1.观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。
2.模式包含四个角色:目标又称为主题,它是指被观察的对象;具体目标是目标类的子类,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;观察者将对观察目标的改变做出反应;在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致。
3.观察者模式的主要优点在于可以实现表示层和数据逻辑层的分离,并在观察目标和观察者之间建立一个抽象的耦合,支持广播通信;其主要缺点在于如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间,而且如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
转:https://learnku.com/docs/laravel-kernel/design-mode-observer-mode/6919
装饰模式描述:
装饰模式能够实现动态的为对象添加功能,是从一个对象外部来给对象添加功能。通常有两种方式可以实现给一个类或对象增加行为:
1.继承机制,使用继承机制是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。
2.组合机制,即将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,我们称这个嵌入的对象为装饰器 (Decorator)。
显然,为了扩展对象功能频繁修改父类或者派生子类这种方式并不可取。在面向对象的设计中,我们应该尽量使用对象组合,而不是对象继承来扩展和复用功能。装饰器模式就是基于对象组合的方式,可以很灵活的给对象添加所需要的功能。装饰器模式的本质就是动态组合。动态是手段,组合才是目的。总之,装饰模式是通过把复杂的功能简单化,分散化,然后在运行期间,根据需要来动态组合的这样一个模式。
装饰模式定义:
装饰模式 (Decorator Pattern) :动态地给一个对象增加一些额外的职责 (Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器 (Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为 “油漆工模式”,它是一种对象结构型模式。
装饰模式的优点:
1.装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
2.可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
3.通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
模式结构和说明:
1.聚合关系用一条带空心菱形箭头的直线表示,上图表示 Component 聚合到 Decorator 上,或者说 Decorator 由 Component 组成。
2.继承关系用一条带空心箭头的直接表示
3.看懂 UML 类图请看这个文档:https://designpatterns.readthedocs.io/zh_CN/latest/read_uml.html
Component:
组件对象的接口,可以给这些对象动态的添加职责;
ConcreteComponent:
具体的组件对象,实现了组件接口。该对象通常就是被装饰器装饰的原始对象,可以给这个对象添加职责;
Decorator:
所有装饰器的父类,需要定义一个与 Component 接口一致的接口 (主要是为了实现装饰器功能的复用,即具体的装饰器 A 可以装饰另外一个具体的装饰器 B,因为装饰器类也是一个 Component),并持有一个 Component 对象,该对象其实就是被装饰的对象。如果不继承 Component 接口类,则只能为某个组件添加单一的功能,即装饰器对象不能再装饰其他的装饰器对象。
ConcreteDecorator:
具体的装饰器类,实现具体要向被装饰对象添加的功能。用来装饰具体的组件对象或者另外一个具体的装饰器对象。
装饰器的示例代码:
1.Component 抽象类,可以给这些对象动态的添加职责
abstract class Component
{
abstract public function operation();
}
2.Component 的实现类
class ConcreteComponent extends Component
{
public function operation()
{
echo __CLASS__ . '|' . __METHOD__ . "\r\n";
}
}
3. 装饰器的抽象类,维持一个指向组件对象的接口对象, 并定义一个与组件接口一致的接口
abstract class Decorator extends Component
{
/**
* 持有Component的对象
*/
protected $component;
/**
* 构造方法传入
*/
public function __construct(Component $component)
{
$this->component = $component;
}
abstract public function operation();
}
4. 装饰器的具体实现类,向组件对象添加职责,beforeOperation (),afterOperation () 为前后添加的职责。
class ConcreteDecoratorA extends Decorator
{
//在调用父类的operation方法的前置操作
public function beforeOperation()
{
echo __CLASS__ . '|' . __METHOD__ . "\r\n";
}
//在调用父类的operation方法的后置操作
public function afterOperation()
{
echo __CLASS__ . '|' . __METHOD__ . "\r\n";
}
public function operation()
{
$this->beforeOperation();
$this->component->operation();//这里可以选择性的调用父类的方法,如果不调用则相当于完全改写了方法,实现了新的功能
$this->afterOperation();
}
}
class ConcreteDecoratorB extends Decorator
{
//在调用父类的operation方法的前置操作
public function beforeOperation()
{
echo __CLASS__ . '|' . __METHOD__ . "\r\n";
}
//在调用父类的operation方法的后置操作
public function afterOperation()
{
echo __CLASS__ . '|' . __METHOD__ . "\r\n";
}
public function operation()
{
$this->beforeOperation();
$this->component->operation();//这里可以选择性的调用父类的方法,如果不调用则相当于完全改写了方法,实现了新的功能
$this->afterOperation();
}
}
5. 客户端使用装饰器
class Client
{
public function main()
{
$component = new ConcreteComponent();
$decoratorA = new ConcreteDecoratorA($component);
$decoratorB = new ConcreteDecoratorB($decoratorA);
$decoratorB->operation();
}
}
$client = new Client();
$client->main();
6. 运行结果
oncreteDecoratorB|ConcreteDecoratorB::beforeOperation
ConcreteDecoratorA|ConcreteDecoratorA::beforeOperation
ConcreteComponent|ConcreteComponent::operation
ConcreteDecoratorA|ConcreteDecoratorA::afterOperation
ConcreteDecoratorB|ConcreteDecoratorB::afterOperation
装饰模式需要注意的问题:
1.一个装饰类的接口必须与被装饰类的接口保持相同,对于客户端来说无论是装饰之前的对象还是装饰之后的对象都可以一致对待。
2.尽量保持具体组件类 ConcreteComponent 的轻量,不要把主逻辑之外的辅助逻辑和状态放在具体组件类中,可以通过装饰类对其进行扩展。 如果只有一个具体组件类而没有抽象组件类,那么抽象装饰类可以作为具体组件类的直接子类。
适用环境:
1.需要在不影响组件对象的情况下,以动态、透明的方式给对象添加职责。
2.当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时可以考虑使用装饰类。
转:https://learnku.com/docs/laravel-kernel/design-mode-decoration-mode/6918
外观模式描述:
外观模式 (Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式。
Laravel 中我们常用到的 Route、Redis、Auth 这些 Facade 就是外观模式的具体实现, 在 Laravel 中设计了多个外观类,每个外观类继承自统一的抽象外观类,在抽象外观类里提供了通过外观类访问其背后子系统的基础方法。
对于新的业务需求,不要修改原有外观类,而应该增加一个新的具体外观类,由新的具体外观类来关联新的子系统对象,同时通过修改配置文件来达到不修改源代码并更换外观类的目的。
下面是一个简单的外观模式的例子,并没有引入抽象外观类,在介绍 Laravel Facade 的文章中我们会看到 Laravel 里提供了一个抽象外观类从而让我们能够方便的根据需要增加新子系统的外观类,并让外观类能够正确代理到其对应的子系统 (或者叫服务)。
模式结构:
外观模式包含如下角色:
1.Facade 外观角色
2.SubSystem 子系统角色
代码示例
<?php
class Client
{
public function main()
{
(new Facade)->operation();
}
}
class Facade
{
private $systemA;
private $systemB;
public function __construct()
{
$this->systemA = new SystemA;
$this->systemB = new SystemB;
}
public function operation()
{
$this->systemA->operationA();
$this->systemB->operationB();
}
}
class SystemA
{
public function operationA()
{
//
}
}
class SystemB
{
public function operationB()
{
//
}
}
模式分析:
根据 “单一职责原则”,在软件中将一个系统划分为若干个子系统有利于降低整个系统的复杂性,一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小,而达到该目标的途径之一就是引入一个外观对象,它为子系统的访问提供了一个简单而单一的入口。 - 外观模式也是 “迪米特法则” 的体现,通过引入一个新的外观类可以降低原有系统的复杂度,同时降低客户类与子系统类的耦合度。 - 外观模式要求一个子系统的外部与其内部的通信通过一个统一的外观对象进行,外观类将客户端与子系统的内部复杂性分隔开,使得客户端只需要与外观对象打交道,而不需要与子系统内部的很多对象打交道。 - 外观模式的目的在于降低系统的复杂程度。 - 外观模式从很大程度上提高了客户端使用的便捷性,使得客户端无须关心子系统的工作细节,通过外观角色即可调用相关功能。
外观模式的缺点:
不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。
在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了 “开闭原则”。
模式扩展:
1.一个系统有多个外观类
在外观模式中,通常只需要一个外观类,并且此外观类只有一个实例,换言之它是一个单例类。在很多情况下为了节约系统资源,一般将外观类设计为单例类。当然这并不意味着在整个系统里只能有一个外观类,在一个系统中可以设计多个外观类,每个外观类都负责和一些特定的子系统交互,向用户提供相应的业务功能。
2.不要试图通过外观类为子系统增加新行为
不要通过继承一个外观类在子系统中加入新的行为,这种做法是错误的。外观模式的用意是为子系统提供一个集中化和简化的沟通渠道,而不是向子系统加入新的行为,新的行为的增加应该通过修改原有子系统类或增加新的子系统类来实现,不能通过外观类来实现。
3.抽象外观类的引入
外观模式最大的缺点在于违背了 “开闭原则”,当增加新的子系统或者移除子系统时需要修改外观类,可以通过引入抽象外观类在一定程度上解决该问题,客户端针对抽象外观类进行编程。对于新的业务需求,不修改原有外观类,而对应增加一个新的具体外观类,由新的具体外观类来关联新的子系统对象,同时通过修改配置文件来达到不修改源代码并更换外观类的目的。
总结:
1.在外观模式中,外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式。
2.外观模式包含两个角色:外观角色是在客户端直接调用的角色,在外观角色中可以知道相关的 (一个或者多个) 子系统的功能和责任,它将所有从客户端发来的请求委派到相应的子系统去,传递给相应的子系统对象处理;在软件系统中可以同时有一个或者多个子系统角色,每一个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能。
3.外观模式要求一个子系统的外部与其内部的通信通过一个统一的外观对象进行,外观类将客户端与子系统的内部复杂性分隔开,使得客户端只需要与外观对象打交道,而不需要与子系统内部的很多对象打交道。
4.外观模式主要优点在于对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易,它实现了子系统与客户之间的松耦合关系,并降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程;其缺点在于不能很好地限制客户使用子系统类,而且在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了 “开闭原则”。
5.外观模式适用情况包括:要为一个复杂子系统提供一个简单接口;客户程序与多个子系统之间存在很大的依赖性;在层次化结构中,需要定义系统中每一层的入口,使得层与层之间不直接产生联系。
转:https://learnku.com/docs/laravel-kernel/design-mode-appearance-mode/6917
请教 在
国内用什么翻墙使用谷歌?上外网神器Ghelper插件详解中评论你好,我也遇到了安装完右...
Test11 在
laravel查询构造器中whereNotKey,whereKey,firstWhere用法详解中评论<script&g...
博主 在
国内用什么翻墙使用谷歌?上外网神器Ghelper插件详解中评论@西瓜: 每一步都操作完...
西瓜 在
国内用什么翻墙使用谷歌?上外网神器Ghelper插件详解中评论你好 我安装完右上角没有...
博主 在
laravel-admin 添加Excel导入功能中评论这应该算是比较详细了吧,...
唐伯虎 在
laravel-admin 添加Excel导入功能中评论咋导入excel
...博主 在
国内用什么翻墙使用谷歌?上外网神器Ghelper插件详解中评论希望能帮到你 老铁
疾风神术 在
国内用什么翻墙使用谷歌?上外网神器Ghelper插件详解中评论我知道了,是没有打开开发...
Auther ·HouTiZong© 2009-2020 zongscan.com 版权所有ICP证:
粤ICP备20027696号
也可以扫右边的二维码