设计模式之装饰模式(iOS开发,代码用Objective-C展示)
在面向对象编程中有个重要的原则,里氏代换原则:一个软件实体如果使用的是一个父类的话,那么一定适用其子类,而且它察觉不出父类对象与子类对象的区别。也就是说,在软件设计里面,把父类替换成它的子类,程序的行为没有变化。简单的说,子类类型必须能替换掉它的父类类型。
就好像继承的概念,子类继承自父类,那么子类可以以父类的身份出现。有这样一个问题,在面向对象设计中,一个是鸟类,一个是企鹅类,如果鸟是可以飞得,企鹅不会飞,那么企鹅是鸟么?企鹅可以继承自鸟类么?
需要面向对象设计,那么意味着,子类拥有父类所以非private的属性和行为,鸟会飞,而企鹅不会,所以企鹅是鸟,但它不能继承自上面那个会飞的鸟类,抽象出一个更高的鸟类,然后分为会飞的鸟子类、不会飞的鸟子类,企鹅应该继承自不会飞的鸟子类。
这因为有了里氏代换原则,才使得继承复用成为可能,只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也可以在父类的基础上增加新的行为。正是由于子类的可替换性才使得父类类型模块在无需修改的情况下就能扩展,这是前面提到的,对扩展的开放,对修改的封闭(ocp原则)。
装饰模式
穿衣问题,要求写一个给人搭配不同服饰的系统,那种可以换各种各样衣服和裤子的服饰系统,如下图:

首先排除这样的结果设计,如果我需要新增加超人的服饰设计,又得更改Person类,很明显违背了开发-封闭原则(ocp,对扩展的开放,对修改的封闭)。其实把这些服饰类写成子类就好,代码结构:

如此,需要增加超人的装扮 ,只需要增加子类即可。不需要对已有的代码进行修改。但是这样还打不到最好,我们需要在控制器里面来开辟诸如"破球鞋"、“垮裤”等对象,将他们一个词一个词的显示出来,就好比是在众目睽睽下穿衣服。
对于这些,应当去优化它们。就可以用到装饰模式:动态的给一个对象添加一些额外的职能,就增加功能来说,装饰模式比添加子类更加灵活。无论是衣服、鞋子、裤子等,其实我们都可以把它理解为对Person的装饰,那么有下图结构:

代码:
Person类:
#import <Foundation/Foundation.h> @interface ZYPerson : NSObject
{
@protected
NSString *_name;
}
- (instancetype)initWithName:(NSString *)name; - (void)display;
@end #import "ZYPerson.h" @implementation ZYPerson
- (instancetype)initWithName:(NSString *)name
{
if (self = [super init]) {
_name = name;
}
return self;
} - (void)display
{
NSLog(@"装扮的%@:",_name);
}
@end
clothing类:
#import "ZYPerson.h" @interface ZYClothing : ZYPerson
@property (nonatomic, strong) ZYPerson *decorate; - (instancetype)initWithDecorate:(ZYPerson *)decorate;
@end #import "ZYClothing.h" @implementation ZYClothing
- (instancetype)initWithDecorate:(ZYPerson *)decorate
{
if (self = [super init]) {
_decorate = decorate;
}
return self;
} - (void)display
{
if (self.decorate) {
[self.decorate display];
}
}
@end
TShirts类:
#import "ZYClothing.h" @interface ZYTShirts : ZYClothing @end #import "ZYTShirts.h" @implementation ZYTShirts
- (void)display
{
[super display];
NSLog(@"大衬衫");
}
@end
Pants类:
#import "ZYClothing.h" @interface ZYPants : ZYClothing @end #import "ZYPants.h" @implementation ZYPants
- (void)display
{
[super display];
NSLog(@"大裤衩");
}
@end
Shoe类:
#import "ZYClothing.h" @interface ZYShoe : ZYClothing @end #import "ZYShoe.h" @implementation ZYShoe
- (void)display
{
[super display];
NSLog(@"破鞋子");
}
@end
viewController里面的代码:
#import "ViewController.h"
#import "ZYPerson.h"
#import "ZYClothing.h"
#import "ZYTShirts.h"
#import "ZYPants.h"
#import "ZYShoe.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib. ZYPerson *person = [[ZYPerson alloc] initWithName:@"晶童鞋"];
ZYClothing *clothing = [[ZYClothing alloc] initWithDecorate:person];
ZYTShirts *shirts = [[ZYTShirts alloc] init];
ZYPants *pants = [[ZYPants alloc] init];
ZYShoe *shoe = [[ZYShoe alloc] init]; //装扮过程,相当于在室内穿衣服,控制器并不知道它是怎么的顺序
shirts.decorate = clothing;
pants.decorate = shirts;
shoe.decorate = pants;
[shoe display]; //第二次装扮
pants.decorate = clothing;
shoe.decorate = pants;
shirts.decorate = shoe;
[shirts display];
}
@end
运行效果图:

装饰模式总结:
我觉得装饰模式,是为已有功能动态的添加更多功能的一种方法。但是到底什么时候用它呢?
在本文的最初设计中,当系统需要添加新功能的时候,是向旧的类中添加新的代码,这些新增的代码通常装饰了原有类的核心职能或主要行为。这种设计方式问题在于,他们在主类中增加了新的字段、新的方法、新的逻辑,从而增加了主类的负责度。而这些新加入的东西仅仅是为了满足一些在某种特定情况下才会执行的特殊行为的需求。
而装饰模式提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包含它所要装饰的对象,因此,当执行特殊行为时,在viewController里就可以根据需求有选择、按顺序的使用装饰功能包装对象了。
所以就有了上面的代码,我可以通过装饰,让person武装到牙齿,也可以只让他穿条内裤。
装饰模式的优点:
- 把类中的装饰功能从类中搬移出去,这样可以简化原有的类。
- 当有效的把类中的核心功能和装饰功能区分开了,可以去除相关类中重复的装饰逻辑。
设计模式之装饰模式(iOS开发,代码用Objective-C展示)的更多相关文章
- iOS开发核心语言Objective C —— 全部知识点总结
本分享是面向有意向从事iOS开发的伙伴及苹果产品的发烧友,亦或是已经从事了iOS的开发人员,想进一步提升者.假设您对iOS开发有极高的兴趣,能够与我一起探讨iOS开发.一起学习,共同进步.假设您是零基 ...
- iOS开发核心语言Objective C —— 所有知识点总结
C和OC对比 OC中主要开发在什么平台上的应用程序?答:可以使用OC开发Mac OS X平台和iOS平台的应用程序 OC中新增关键字大部分是以什么开头?答:OC中新增关键字大部分是以@开头 OC中新增 ...
- iOS开发核心语言Objective C —— 面向对象思维、setter和getter方法及点语法
本分享是面向有意向从事iOS开发的伙伴们.或者已经从事了iOS的开发人员.假设您对iOS开发有极高的兴趣,能够与我一起探讨iOS开发.一起学习,共同进步.假设您是零基础,建议您先翻阅我之前分享的iOS ...
- IOS开发-代码规范
代码风格的重要性对于一个团队和项目来说不言而喻.网上有许多 Objective-C 的代码风格,但这份简洁而又最符合苹果的规范,同时有助于养成良好的代码习惯,也是我们团队一直遵循的代码风格. 写法没有 ...
- iOS开发 代码 或 <Home+Power>截屏
1. 截屏的两种简单方法, 注意这两种截图方法,都必须在视图完全加载完成后才能截图,即在 viewDidAppear 方法之后截屏,否则无法得到想要的截屏效果 (1) 利用绘图方法 renderI ...
- iOS开发代码规范(通用)
1. 关于命名 1> 统一要求 含义清楚,尽量做到不需要注释也能了解其作用,若做不到,就加注释 使用全称,不适用缩写 2> 类的命名 大驼峰式命名:每个单词的首字母都采用大写字母 例子:M ...
- iOS开发代码规范
1.关于命名 1.1统一要求 含义清楚, 尽量做到不需要注释也能了解其作用,若做不到,就加注释 使用全称不使用缩写 1.2类的命名 大驼峰式命名:每一个单词的首字母都采用大写字母例子: MFHomeP ...
- iOS - 开发代码部分规范
1. 关于命名 1.1 统一要求 含义清楚,尽量做到不需要注释也能了解其作用,若做不到,就加注释 使用全称,不适用缩写 1.2 类的命名 大驼峰式命名:每个单词的首字母都采用大写字母 例子:MFHom ...
- IOS开发代码分享之获取启动画面图片的string
http://www.jb51.net/article/55309.htm 本代码支持 iPhone 6 以下. 支持 iPhone 及 iPad ? 1 2 3 4 5 6 7 8 9 10 11 ...
- [IOS 开发代码]UIImage+Blur 网络图片模糊用法
UIImage-Helpers 网络图片模糊用法 float quality = .00001f; float blurred = .5f; NSURL *url = [NSURL URLW ...
随机推荐
- 进阶之路(中级篇) - 016 温湿度传感器DHT11
如果想使用 Arduino 开发板驱动 DHT11 来获取温湿度的时候建议使用第三方的库,这样可以加快程序的开发速度,而且不容易出错,下面的代码我已经安转了第三方的库了.详细的安装方法请参考极客先锋的 ...
- Linux操作系统中文件结构stat中st_size的说明以及对于文件中洞(Holes)的理解
文件stat结构体中st_size成员 对于所有的文件类型,st_size成员对其中的普通文件.目录以及符号链接有实在的意义.其中,对于普通文件而言,st_size记录了该文件的实际大小:对于目录而言 ...
- Swift 封装
前言 封装主要有两大目的:一是为了我们使用数据更加方便,二是为了数据保护. 1.Swift 访问修饰符 在 Swift 语言中,访问修饰符也分为三类,分别是 private.internal.publ ...
- 一步一步掌握java的线程机制(二)----Thread的生命周期
之前讲到Thread的创建,那是Thread生命周期的第一步,其后就是通过start()方法来启动Thread,它会执行一些内部的管理工作然后调用Thread的run()方法,此时该Thread就是a ...
- The password supplied with the username Domain\UserName was not correct. Verify that it was entered correctly and try again
起因 今天想进入SharePoint 2013 Central Administration创建一个WebApplication,尽然发生了错误: The password supplied with ...
- Linux中在防火墙中开启80端口的例子
最近自己在学习Linux.搭建一个LNMP环境.在测试时一切都好.然后重启Linux后.再次访问网站无法打开.最终原因是在防火墙中没有加入 80 端口的规则.具体方法如下: 在CentOS下配置ipt ...
- Python3 命令行参数
Python 提供了 getopt 模块来获取命令行参数. $ python test.py arg1 arg2 arg3 Python 中也可以所用 sys 的 sys.argv 来获取命令行参数: ...
- Eclipse Oxygen创建maven web项目(二)
配置项目依赖库及第三方工具. 如示例的pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi ...
- 记一次Linux系统安装的异常(AMI配置)
一.背景 安装Linux系统(Debian 8.8),老是失败,卡在Install Software (12%的位置) 二.解决? 网上搜到的解决方法 1. 安装过程中不要拔网线(我没拔) 2. 安装 ...
- Desugar Scala(17) -- Option和for,以及脑子里发生的事情
欢迎关注我的新博客地址:http://cuipengfei.me/blog/2014/08/30/options-for/ Scala里的forkeyword是个非常有趣的东西. 能够用来把多层嵌套f ...