设计模式

来源:互联网 发布:sql 表字段添加注释 编辑:程序博客网 时间:2024/06/17 13:05

适配器模式

1,概述

适配器模式, Adapter Pattern, 指的是将一个接口转换为另一个期望的接口. 目的是使得原本不匹配的接口变得兼容.

例如, 我现在需要插入网线上网, 但是我的笔记本没有网线插口, 只有usb插口. 此时就需要一个转换器, 将usb接口转换为网线插口. 这个转换器这就是一个适配器.

在上面的例子中, 有三个角色:

  • 适配器, 就是接口转换器.
  • 待适配实例, 就是我的笔记本电脑, 需要适配一个网线插口.
  • 适配目标, 就是我的网线插口. 就是待适配实例需要适配的目标.

以此为例, 编码演示.

2,先看UML结构

图中所示, 适配器使用一个属性保存待适配的实例, 同时实现了适配目标中定义的方法.
就是我的适配器, 需要能够找到我的笔记本电脑, 同时需要满足插网线的操作.

3,再看代码演示

代码演示中, 我们会定义一个适配目标接口, 就是网线操作接口. 以及一个适配器类, 和一个笔记本电脑类. 用来演示, 适配器使得笔记本可以使用网线上网的案例.

3.1,目标接口, 网线操作接口

一个定义了RJ45(网线口)标准的网线操作接口. 我们的目标就是要适配该接口.

3.2,笔记本类,我们的待适配目标

一个待适配实例类, 我们的笔记本目前仅仅支持USB传输, 不支持RJ45传输.
因为不能更改网线和笔记本, 因此需要中间的适配器.

3.3,适配器类

适配类, 实现了目标接口, 因此在使用目标时, 直接使用该适配器即可.
语法上, 实现了接口, 重写了接口的方法, 由待适配示例完成适配目标方法即可.

3.4,使用适配器

当我们需要在笔记本上使用RJ45网线时, 直接使用适配器,就是将网线插在适配器上. 由于是笔记本的适配器,因此 适配器是可以找到电脑笔记本实例的. 在适配器内部完成了转换工作.

4,分析

由于待适配实例中方法与适配目标接口方法不一致问题, 由适配器完成转换. 此时适配器是专门为电脑设计的, 因此适配器需要获取电脑笔记本对象. 也就意味着, 如果待适配实例发生改变, 则我们的适配器也要随之更新, 构建新的适配器出现.

上面代码演示的实现方式, 通常叫做对象适配模式 , 体现就是适配器的属性引用了待适配的示例.
除了对象适配模式, 还有一种典型的类适配模式来实现适配模式, 但通常需要使用多继承机制, 由于语言不支持多继承特性, 暂且不与演示.

5,结语

类似的模式有, 代理模式, 装饰者模式 等. 请持续关注呢.
一家之言, 欢迎补充, 讨论, 拍砖.
如有帮助, 谢谢转发哈!​

单例模式

1,概述

单例模式, singleton pattern, 顾名思义, 就是一个类仅仅可以存在一个实例. 故称单例模式, 也是一个规范创建对象的一种设计模式.

使用场景, 是程序周期中, 使用某个类的一个对象, 即可完全完成全部任务时, 可以选择使用单例模式设计类. 进而保证尽可以有一个实例.

实操时, 通常有懒惰式和登记式单例实现(这个受限与应用语言语法).

2,懒惰式实现

实现思路是:
通过私有化构造方法和克隆方法, 使得类不能在类外实例化, 实例化好的对象也不能被克隆. 就意味着不能任意生成新对象.

同时提供一个静态方法, 生产该类的对象. 该静态方法中实现了如果对象已经实例化过, 则直接返回其对象的引用. 反之, 如对象没有被实例化过, 执行实例化, 存储起来. 再返回.

2.1,单例实现的Dao

以上代码可以做个标签辅助记忆: 三私一公

2.2,多次获取对象演示

由结果的输出#1, 可以看出, 三个变量引用的是同一个对象. 单例目的达到!

该实现之所以被称之为是懒惰式, 是因为, 对象的生成是在第一次使用时才实例化的. 如果不使用, 则不实例化. 因此成为懒惰方式. (可想的勤劳方式就是在类初始化时, 同时实例化该类的对象, 用的时候直接返回即可. 但PHP语法不支持private static $intance=new self()这类的语法, 因此就只有这个懒惰式了)
懒惰式的优势, 在于如果没有实例化, 则节省资源.

3,登记式实现

所谓的登记式, 指的是除了需要单例效果的类之外, 提供一个其他代码完成对某些类的单例等级. 通常使用类名与对象映射的方式存储. 如果类登记过对象, 则使用时直接返回, 否者实例化,存储再返回.
说白了, 就是将懒惰式中instance()方法的逻辑, 搬到类外, 其他代码负责完成和记录.

3.1 登记类

可以视为一个工厂类, 专门用来生产单例对象. 生产完毕, 存储起来, 下次再用, 直接返回.

3.2,测试

由结果可知, 是同一个对象.

代理式的由来, 就是原本是Dao的功能, 被Singleton这个代理完成了.
其优势是将功能与单例在Dao中解耦了. 同时Dao也更加灵活, 可以适用于单例和非单例的情况了.

4,结语

单例模式这种, 适应的情况非常典型. 因此不要可以去使用, 只有确实一个对象就能搞定的情况下, 才可以使用. 实际操作中, 数据库连接往往使用该设计模式. 因为一个连接可以执行所有的SQL了. 情况吻合, 隐藏可以使用单例模式.

一家之言, 欢迎讨论, 拍砖.

PS: 至此, 创建型设计模式就完毕了.
但是结构型, 行为型, 架构型还会继续.

工厂方法模式

1 概述

通常有三种工厂模式可用: 简单工厂, 工厂方法, 抽象工厂。他们都是在解决动态创建对象的问题。针对于不同的情况和场景,选择合适的工厂模式。
上文中介绍简单工厂模式(输入”简单工厂模式”可以查看)。

本文讨论工厂方法模式(Factory Method Pattern)。

首先, 无论是简单工厂还是工厂方法模式,解决的是同一类问题,动态创建对象的问题。

但简单工厂模式存在一个问题, 如果需要生产的对象增加了, 就需要更改工厂类的方法, 不满足开放-封闭的原则.也使得维护难度增大.

如何使得工厂的维护变得容易, 可以满足开放-封闭原则呢?
so easy, 将具体的生产工作下放到工厂的子类中。就是一个工厂存在多个子工厂,具体的产品的生产工作,由对应的子工厂完成,而上级工厂仅仅负责抽象具体功能接口。此时, 一旦增减了生产的产品,我们的工厂只需要提供对应的子厂即可。这种设计就是工厂方法模式。
由此可见,如果生产任务不复杂的工厂,简单工厂就ok了。但是生产任务复杂的工厂,就需要我们的工厂变得健壮,就需要由简单工厂升级为工厂方法模式。

请看演示。 演示内容有:上级工厂,子工厂(具体产品工厂),产品接口,产品,生产演示。

2 UML

先通过UML了解,上级工厂,子工厂,产品之间的关系:

图上可以看到,每种产品都有对应的工厂来生产。而所有的子工厂都实现了工厂接口,完成工厂规范。
绿色的表示,当新增了产品时,同时增加对应工厂即可。解耦,满足开放-封闭原则。

3 代码演示

3.1 工厂接口及工厂子类

代码演示了2个子工厂和一个工厂接口(实操中也可以由抽象类实现)。工厂接口仅仅负责规范工厂应该具有的操作,而工厂子类完成具体产品的生产。

3.2 日志接口及日志类

3.3 生产演示

3.4 增加新产品及工厂

代码演示了,一旦需要增加额外的产品,我们同时增加对应的工厂来生产。由于工厂都实现的接口,因此新的产品的生产也满足原有规范,可以直接融入到之前的业务逻辑中。

4 分析

和简单工厂一样,都是解决动态生产对象的问题。只是工厂方法模式,使得工厂的规模变大了,由原来的一个车间升级为由多个子车间构成的大规模工厂了。而且允许扩展子工厂。因此,在生产相对复杂,繁多的产品时,工厂方法模式要更加适合些。而且扩展之后,不需要修改之前的代码,满足开放-封闭和原则。

最后吐槽一句,工厂方法其实就是将简单工厂的case语句变由一个个的类方法来实现了。
该模式被GOF(Gang Of Four,四人组)写到了《设计模式》一书中。被发扬光大了。

5 结语

一家之言,欢迎讨论,拍砖!
如有帮助, 请帮忙转发!

构建者模式

1    概述

构建者模式(Builder Pattern), 是一种获取对象的设计模式.

其设计思路是将对象本身与构建对象操作分开处理. 通过对象的构建操作来获取需要的对象. 实操中使用很广泛, 例如, SELECT语句对象的构建, SELECT查询条件where的构建, url对象的构建等.

就以SELECT语句的构建为例,  SELECT语句具有结构复杂, 拼凑多样性的问题. 思考, SELECT语句由多个子句, From, where, …等构成. 同时, 在使用时, 还允许某些子句存在, 某些子句不存在. 可见, 构建SELECT语句的过程, 就是一个相对复杂操作. 此时就可以将语句对象和语句对象的构建分开处理, 这种设计,就称之为: 构建者模式. 而实际中, 多数的SELECT语句的获取, 都会采用该设计模式实现. 非常实用.

需要采用构建者模式来构建的对象. 通常是对象的内部结构会有不同的复杂变化. 因此, 将处理这些复杂部分代码, 独立出来, 不加入到对象的内部结构中. 这样, 对象本身就和对象的创建过程解耦了. 对象本身, 不再去关注构建的过程, 仅仅需要处理对象本身的功能即可.

以上的设计, 就满足面向对象的设计模式原则中: 合成复用的原则.

 

2    代码演示

就以构建SELECT语句为例.

我们的最终目标是得到对象Query, 一个包含了SELECT语句的对象, 可以执行该SELECT语句, 返回匹配的记录 的查询对象.

而SELECT的拼凑, 复杂, 我们将拼凑的代码抽取, 有QueryBuilder实现.

 

2.1      Query类

 

Query类对象, 有一个SQL语句属性, 保存拼凑好的SELECT语句. 在实例化时, 获取.

Query类对象, 在实际工作中, 完成执行SQL依据, 获取结果的工作, all(), one()方法就是用来执行和获取结果的. 功能性代码省略.

 

1.2.2      QueryBuild类, Query对象构建类

 

QueryBuild的主要作用,就是拼凑SELECT语句的各个部分. 有具体的每个方法负责拼凑语句的各个部分. 示例中演示了简单的查询字段和from表名的拼凑. 用户需要调用QueryBuild对象的field(), table()方法,完成设置字段和表名.

同时提供build()方法, 用来基于设置好的子句部分, 拼凑完整的SELECT语句, 并实例化Query对象.

 

2.3      构建演示

后续就是Query对象的具体操作.

3    分析

通过以上的代码展示, 大家注意Query对象的获取方式.

是通过QueryBuilder对象的build方法获取的.

这么设计的原因, 就是SELECT语句的结构较为复杂, 将拼凑和最终的使用分离实现.

这就是采用构建者模式获取对象的方式.

额外的, 具体功能的实现, 不是本文的内容, 故大部分功能的代码略. 仅仅展示了构建者模式需要的代码.

 

请关注 微信公众号: 小韩说理

 

原型模式

概述

原型模式(prototype pattern), 是获取对象的一种设计模式. 设计思想是通过复制(克隆)已有对象, 得到新的对象. 而这个已有对象, 就是复制(克隆)而形成才新对象的原型. 故称之原型模式.

核心语法, 就是clone.

使用克隆获取新对象的方式的优势通常是, 在实例化对象相对复杂时, 通常指的是需要做大量的构造初始化计算时, 可以采用克隆(复制)的方式得到新对象, 提升获取对象的效率.

代码演示

定义可以添加原型和利用原型克隆对象的方法

 

将需要克隆的对象原型加入工厂

 

需要对象时, 通常工厂克隆生成

 

代码分析

工厂类的静态属性存储加入工厂的原型对象, 等待被克隆.

提供生成对象的接口getObject(), 通过传递参数类名, 获取对应的对象.

对象是通过clone而形成的.

 

采用该方法, 在new操作复杂(构造方法复杂)时, clone获取对象的效率会高些, 做了一个小测试:

时间对比, clone的效率会高些.

 

深浅克隆

在执行clone时, PHP默认采用浅克隆, 就是如果是对象类型的属性, 则仅仅是引用复制, 而不是复制对象本身.

如果需要将对象本身也同时克隆. 则需要定义对象的__clone()方法, 对对象类型的属性做进一步的clone操作.

 

laravel的核心概念:服务提供者provider解析

摘要

上两篇中, 说了IoC服务容器 和 DI依赖注入.

我们知道laravel和核心就是一个IoC容器, 被称之为服务容器. 那么作为IoC容器, 就必须要有绑定对象生成器的工作.  在laravel中是服务提供者来项服务容器中绑定对象生成器的.

下面就继续说说, laravel的核心架构中的 服务提供者.provider

服务提供者, 就是负责在laravel的IoC容器中绑定对象生成器的代码. 在laravel中称之为服务提供者.

注册服务提供者

laravel要知道, 哪些有服务提供者来负责绑定IoC容器, 采用的办法是, 在配置文件中, 将所有的服务提供者配置好, 所有的服务提供者, 都在 config/app.php中配置, 示例代码如下 : config/app.php

上面代码的每一行, 都注册一个一个服务提供者.

 

服务提供者绑定对象生成器到IoC服务容器

那么服务器提供者, 是如何绑定IoC容器中的对生成器呢?

每个服务器提供者对象, 都要实现一个register()方法, 该方法会被laravel自动调用. register()方法, 就是用来向IoC服务容器绑定对象生成器的.

以lluminate\Cache\CacheServiceProvider这个提供者为例, 查看其register方法, 位于:vendor/laravel/framework/src/Illuminate/Cache/CacheServerProvider.php文件中, 代码如下

在register()方法中, 通过$this->app访问到IoC服务容器. 然后调用该容器的singleton()方法, 绑定单例对象生成器.

可见, 当register()被调用时, 该服务提供者就会向IoC服务容器绑定内容. 绑定完毕后, laravel中就可以使用该类的对象了. make()也好, 自动注入依赖也罢都可以了.

结语

这样, 服务提供者的核心任务, 绑定对象的生成代码到IoC服务容器这个任务就完成了. 这就是laravel中的, 服务提供者provider的概念.

一家之言, 欢迎讨论拍砖.

 

最新的内容, 可以通过关注我的微信公众号: 小韩说理 获取

%e5%b0%8f%e9%9f%a9%e8%af%b4%e7%90%86

IoC容器:依赖注入(DI)的自动化实现

概述

在上一篇中, 讲解了依赖注入(DI)( http://www.hellokang.net/2016/11/08/ioc-di/).

先留意下面的代码:

可见, 在注入时, 需要实例化好所依赖的对象, 再传递到Hero类中. 虽然通过依赖注入解决了解耦的问题,  但在实际使用时, 比较麻烦, 因为每次都需要手动实例化依赖, 再传递. 这对于复杂大量的依赖关系, 手动解决明显力不从心. 因此, 实际项目中需要一个自动化的依赖注入管理机制, 这就是IoC容器.

IoC容器, 一个封装了依赖注入DI的框架, 实现了动态创建注入依赖对象, 管理依赖关系, 管理对象生命周期等功能.

核心实现, 一般分为绑定(注册)对象生成器 创建对象注入依赖, 两个核心步骤 我们逐一去看.

绑定(注册)对象生成器

绑定: 指的是将类与生成类对象的代码记录, 对应绑定起来. 这样, 在需要某个类的对象时, 直接执行类对应的生成代码, 就可以得到该类的对象了.

上代码: 同样需要讲解依赖注入是的示例类: Hero, Sword, Gun

 

再看容器类:本节重点

注意其中的bind方法, 就是本小节所说的绑定(注册)对象生成器的实现.

bind方法需要两个参数:

  • 第一个就是类的标志, 通常就是带有命名空间的类名称即可. 例如 path\to\Sword, path\to\Hero.
  • 第二个参数就是该类对应的生成器. 所谓生成器, 其实就是一段代码, 可以生成类对象的代码而已, 说白了就是执行new的代码. 可以是匿名函数, 函数, 类方法等可执行结构都是可以的.

这样其实就是, 要用户提供类 和 该类对象的生成代码, 将其对应, 需要该类对象时执行. 但是注册时, 并不调用生成类的代码, 而仅仅是, 先存储起来. 本示例实现中,就存储到了::$generator_list数组中.

绑定示例代码:

调用bind()方法, 提供类名和实例化类对象的匿名函数. 我们的容器就会将类与生成器记录下来, 等着需要时生成.

再看如何创建对象

完善IoC_Container类的代码, 增加创建对象的方法

注意其中的make方法, 就是用来生成对象的方法. 该方法要获取需要的类, 然后调用该类之前注册绑定的生成器函数, 来获取对象.

通过make生成类对象:

这样, 就可以生成两个Hero对象, 一个Sword类对象被注入, 另一个Gun类对象被注入.结果:

这就是IoC容器的两个基本步骤, 1绑定, 2创建.

小结

可见, IoC其实就是一个功能高级点的工厂类而已, 用于生产类的对象. 目前为止, 稍微高级的地方,就是预先将生产类对象的代码, 先绑定了而已. 需要的时候, 在执行生成对象.

而真正解决了依赖问题的, 还是上篇说的DI依赖注入. 而不是这个容器. 容器仅仅是让生成代码更加规范, 方便了而已.

IoC容器, 到此, 基本功能有了, 但是实操时, 往往还需要再升级. 主要升级的方面, 就是自动判断所需要的依赖, 然后生产创建对象时 进行依赖注入. 下面接着聊.

自动依赖注入

上面的例子的make()实现中, 需要手动将Hero需要的参数传递到Make中. 实际的IoC容器, 比这要高级些.

思考我们定义的Hero类的构造方法, 我们使用了类型约束的语法, 限制参数类型:

那就意味着, 我们是知道Hero类所依赖的对象类型的, 因此我们可以自动根据这个类型, 从已注册的类中, 将Hero需要的Weapon注入(传递)到Hero的构造方法中.

语法上, 我们需要 反射Reflectoin机制, 来了解我们构造方法的参数类型, 去做逻辑判断, 示例代码如下:

为了方便演示自动依赖注入, 将上面的英雄类的依赖改为对 Sword类的依赖, 这样示例代码会直观简单些, 方便学习, 新的英雄Hero类如下:

在IoC容器类中, 增加自动注入的方法:

上面的代码, 先利用反射类对象, 获取类的构造方法; 再利用方法反射对象,获取构造方法的参数列表; 再利用参数的反射对象, 获取参数的类型, 这样,就可以知道参数要求是Sword类. 那么就可以获Sword类对象, 传递给hero类, 将依赖的Sword类注入到Hero类构造方法中去了.

该方法仅仅是一个语法的演示 , 演示如何使用反射完成自动类型判断. 实操中方法的完整性要增加很多操作, 不是上面几行代码就可以完成的.

实际操作中, 会将通用的规范的类, 使用这种IoC容器, 自动依赖注入. 而一些相对特殊规则的类, 可以使用上面的make方法, 解决问题.

那到此, 关于依赖注入的实操中最常见的实现IoC容器, 就说到这里了.

结语

理解了什么是IoC容器, 本文的目的就达到了. 实际使用中(例如laravel)IoC容器的方法会有很多, 例如绑定构造器, 绑定对象实例, 绑定单例, 绑定接口实现等. 具体的使用就要到具体的框架或者产品中看了.

有了IoC容器, 框架的底层, 仅仅提供容器相关的结构, 功能上的模块, 都是容器中注册和生成的.

代码中提到的反射机制语法, 如果大家感兴趣, 可以关注我的PHP基础语法视频系列, 有相关的介绍.

原创粉丝点击