导语| 继上篇【快收藏!最全GO语言实现设计模式】,本文继续列出GO语言实现的经典设计模式示例,每个示例都精心设计,力求符合模式结构,可作为日常编码参考,同时一些常用的设计模式融入了开发实践经验总结,帮助大家在平时工作中灵活运用。
解释器模式用于描述如何使用面向对象语言构成一个简单的语言解释器。在某些情况下,为了更好地描述某一些特定类型的问题,我们可以创建一种新的语言,这种语言拥有自己的表达式和结构,即文法规则,这些问题的实例将对应为该语言中的句子。此时,可以使用解释器模式来设计这种新的语言。对解释器模式的学习能够加深我们对面向对象思想的理解,并且掌握编程语言中文法规则的解释过程。
定义一个解析特征值的语句解释器,提供是否包含特征值的终结表达式,并提供或表达式与且表达式,同时,生成南极洲特征判断表达式,及美国人特征判断表达式,最后测试程序根据对象特征值描述,通过表达式判断是否为真。
(一)概念
适配器模式是一种结构型设计模式,它能使接口不兼容的对象能够相互合作。
适配器可担任两个对象间的封装器,它会接收对于一个对象的调用, 并将其转换为另一个对象可识别的格式和接口。
(二)示例
通过充电宝给不同充电接口的手机充电是一个非常符合适配器模式特征的生活示例;一般充电宝提供USB电源输出接口,手机充电输入接口则分为两类一是苹果手机的lightning接口,另一类是安卓手机的typeC接口,这两类接口都需要通过适配电源线连接充电宝的USB接口,这里USB接口就相当于充电宝的通用接口,lightning或typeC接口要想充电需要通过充电线适配。
桥接是一种结构型设计模式,可将业务逻辑或一个大类拆分为不同的层次结构, 从而能独立地进行开发。
层次结构中的第一层(通常称为抽象部分)将包含对第二层 (实现部分) 对象的引用。抽象部分将能将一些(有时是绝大部分)对自己的调用委派给实现部分的对象。所有的实现部分都有一个通用接口,因此它们能在抽象部分内部相互替换。
简单的说,一个事物存在多个维度的变化点,每一个维度都抽象出一个接口,事物引用这些接口实现整体行为逻辑,而每一个接口都可以存在多个变化的实现。
更简单的一句话:依赖接口编程。
对于一段经历的描述,经历就可能有多种实现,比如旅游经历,探险经历这相当于第一层次的类结构,同时描述旅游经历或探险经历又包含多个维度,比如如何到达目的地,在目的地开展了什么活动等,到达目的地有很多种方式,比如飞机、火车、汽车等;开展的活动又根据地点不同而不同,海边可以冲浪,山地可以攀岩,荒漠可以徒步穿越等;这两个维度的变化点对于描述经历来说相当于第二层次类实现,通过接口被第一层次引用。
这里对于经历描述存在三个维度的变化,
1.经历本身的两个实现:旅游经历与探险经历。
2.交通方式的两个实现:飞机和汽车。
3.开展活动的三个实现:冲浪、攀岩与徒步穿越。
如果用一个类层次去实现就需要2*2*3=12个不同的实现类,如果用桥接模式仅需要2+2+3=7个不同的类,而且两种方式的加速度也不一样,比如增加一个交通方式火车,非桥接模式需要增加2*3*3-12=6个实现类,桥接模式2+3+3-7=1个实现类;桥接模式大大增加了类之间组合的灵活性。
组合是一种结构型设计模式,你可以使用它将对象组合成树状结构,并且能像使用独立对象一样使用它们。
对于绝大多数需要生成树状结构的问题来说,组合都是非常受欢迎的解决方案。组合最主要的功能是在整个树状结构上递归调用方法并对结果进行汇总。
一般来说一个地区统计人口或经济总量,总是通过行政区划一层层上报汇总得出结果,区镇是最低一级行政区划,需要落实统计人口及经济总量的工作,再上一级行政区划需要将所辖区镇的数据汇总统计,以此类推每一级行政区划都需要统计人口与经济总量,就像一个倒过来的树状结构,各级行政区划统一的组件接口是统计人口与经济总量,区镇相当于最底层的叶子节点,中间级别行政区划相当于组合节点;下面代码以苏州市为例;
装饰是一种结构设计模式,允许你通过将对象放入特殊封装对象中来为原对象增加新的行为。
由于目标对象和装饰器遵循同一接口,因此你可用装饰来对对象进行无限次的封装。结果对象将获得所有封装器叠加而来的行为。
地铁进站的过程一般情况下只需要买票,检票进站,但是如果你是带行李,就需要进行安全检查,如果是疫情时期,就需要进行疫情防护检查,比如戴口罩、测量体温等,这里买票进站相当于通用进站流程,安检及防疫检查就相当于加强的修饰行为。
外观是一种结构型设计模式,能为复杂系统、程序库或框架提供一个简单 (但有限) 的接口。
尽管外观模式降低了程序的整体复杂度,但它同时也有助于将不需要的依赖移动到同一个位置。
用户在淘宝电商系统买商品时,只需要选好商品在结算页点击提交即可完成下单;在客户端系统仅需要一个创建订单的方法,但是整个订单的生成需要很多步骤,比如查询用户配送地址,查询商品价格,使用优惠券,扣减商品库存,支付相应价钱等。
享元是一种结构型设计模式,它允许你在消耗少量内存的情况下支持大量对象。
模式通过共享多个对象的部分状态来实现上述功能。换句话来说,享元会将不同对象的相同数据进行缓存以节省内存。
北京出租车调度系统,需要每隔一分钟记录一下全市出租车的位置信息,假设为了提高系统响应速度,近一天的数据需要存储在内存中,每个位置信息包括出租车辆信息及位置信息,位置信息在系统中就是一个(x,y)坐标,车辆信息包括车的号牌,颜色,品牌和所属公司,在调度系统存储的出租车行驶轨迹中,位置是实时在变化的,但车辆信息就可以通过享元模式共用一个对象引用,来减少内存消耗。
代理是一种结构型设计模式,让你能提供真实服务对象的替代品给客户端使用。代理接收客户端的请求并进行一些处理 (访问控制和缓存等), 然后再将请求传递给服务对象。
代理对象拥有和服务对象相同的接口,这使得当其被传递给客户端时可与真实对象互换。
修饰与代理是非常相似的设计模式,都是基于组合设计原则,也就是说一个对象应该将部分工作委派给另一个对象。但两者之间不同点我认为是,修饰器模式总是要执行服务对象,对于执行之前或执行之后结果进行加强,服务对象基本是客户端创建好再嵌套外层的修饰对象;而代理模式不一定执行服务对象,有可能通过缓存,延迟加载等没有访问服务对象,同时服务对象什么时候创建也是由代理类决定的。
房屋中介代理帮助房东卖房子,这个过程就是一个代理模式的过程,中介会收集尽量多的卖房信息,并通过各种渠道发布,同时中介会随时带客户看房,并初步商讨价格,如果达成初步购买意向,才会约房东讨论房屋价格,最后签约卖房;房屋中介与房东都实现卖房接口,中介会提前坐一些前期工作,如果都没问题,才会约房东执行真正的签约卖房流程。
工厂方法模式是一种创建型设计模式,其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
摊煎饼的小贩需要先摊个煎饼,再卖出去,摊煎饼就可以类比为一个工厂方法,根据顾客的喜好摊出不同口味的煎饼。
各种面的煎饼实现
制作各种口味煎饼的工厂方法实现
输出:
抽象工厂定义了用于创建不同产品的接口,但将实际的创建工作留给了具体工厂类。每个工厂类型都对应一个特定的产品变体。
在创建产品时,客户端代码调用的是工厂对象的构建方法,而不是直接调用构造函数 ( new操作符)。由于一个工厂对应一种产品变体,因此它创建的所有产品都可相互兼容。
客户端代码仅通过其抽象接口与工厂和产品进行交互。该接口允许同一客户端代码与不同产品进行交互。你只需创建一个具体工厂类并将其传递给客户端代码即可。
厨师准备一餐时,会分别做吃的和喝的,根据早、中、晚三餐饮食习惯,会分别制作不同的饮食,厨师就相当于抽象工厂,制作三餐的不同烹饪方式就好比不同抽象工厂的实现。
三餐不同厨师接口的实现
不同吃的
不同喝的
输出
生成器是一种创建型设计模式,使你能够分步骤创建复杂对象。
与其他创建型模式不同,生成器不要求产品拥有通用接口。这使得用相同的创建过程生成不同的产品成为可能。
还是摊煎饼的例子,摊煎饼分为四个步骤,1放面糊、2放鸡蛋、3放调料、4放薄脆,通过四个创建过程,制作好一个煎饼,这个摊煎饼的过程就好比煎饼生成器接口,不同生成器的实现就相当于摊不同品类的煎饼,比如正常的煎饼,健康的煎饼(可能用的是粗粮面、柴鸡蛋、非油炸薄脆、不放酱等),生成器接口方法也可以通过参数控制煎饼的大小,比如放两勺面糊,放2个鸡蛋等。
生成器的使用者为了避免每次都调用相同的构建步骤,也可以通过包装类固定几种构建过程,生成几类常用的产品,就好像摊煎饼有几类常卖固定成品,比如普通的,加两个鸡蛋的,不要香菜的等等,这几类固定构建过程提前定制好,直接通过简单工厂方法就直接创建,如果用户再需要细粒度的定制构建,再通过生成器创建。
正常煎饼创建器
健康煎饼创建器
煎饼生成器的封装类-厨师
输出
原型是一种创建型设计模式,使你能够复制对象,甚至是复杂对象,而又无需使代码依赖它们所属的类。
所有的原型类都必须有一个通用的接口, 使得即使在对象所属的具体类未知的情况下也能复制对象。原型对象可以生成自身的完整副本, 因为相同类的对象可以相互访问对方的私有成员变量。
纸质文件可以通过复印机轻松拷贝出多份,设置Paper接口,包含读取文件内容和克隆文件两个方法。同时声明两个类报纸(Newspaper)和简历(Resume)实现了Paper接口,通过复印机(Copier)复印出两类文件的副本,并读取文件副本内容。
输出
单例是一种创建型设计模式,让你能够保证一个类只有一个实例,并提供一个访问该实例的全局节点。
单例拥有与全局变量相同的优缺点。尽管它们非常有用,但却会破坏代码的模块化特性。