在面向对象编程中有个重要的原则,里氏代换原则:一个软件实体如果使用的是一个父类的话,那么一定适用其子类,而且它察觉不出父类对象与子类对象的区别。也就是说,在软件设计里面,把父类替换成它的子类,程序的行为没有变化。简单的说,子类类型必须能替换掉它的父类类型。

就好像继承的概念,子类继承自父类,那么子类可以以父类的身份出现。有这样一个问题,在面向对象设计中,一个是鸟类,一个是企鹅类,如果鸟是可以飞得,企鹅不会飞,那么企鹅是鸟么?企鹅可以继承自鸟类么?

需要面向对象设计,那么意味着,子类拥有父类所以非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武装到牙齿,也可以只让他穿条内裤。

装饰模式的优点:

  1. 把类中的装饰功能从类中搬移出去,这样可以简化原有的类。
  2. 当有效的把类中的核心功能和装饰功能区分开了,可以去除相关类中重复的装饰逻辑。

设计模式之装饰模式(iOS开发,代码用Objective-C展示)的更多相关文章

  1. iOS开发核心语言Objective C —— 全部知识点总结

    本分享是面向有意向从事iOS开发的伙伴及苹果产品的发烧友,亦或是已经从事了iOS的开发人员,想进一步提升者.假设您对iOS开发有极高的兴趣,能够与我一起探讨iOS开发.一起学习,共同进步.假设您是零基 ...

  2. iOS开发核心语言Objective C —— 所有知识点总结

    C和OC对比 OC中主要开发在什么平台上的应用程序?答:可以使用OC开发Mac OS X平台和iOS平台的应用程序 OC中新增关键字大部分是以什么开头?答:OC中新增关键字大部分是以@开头 OC中新增 ...

  3. iOS开发核心语言Objective C —— 面向对象思维、setter和getter方法及点语法

    本分享是面向有意向从事iOS开发的伙伴们.或者已经从事了iOS的开发人员.假设您对iOS开发有极高的兴趣,能够与我一起探讨iOS开发.一起学习,共同进步.假设您是零基础,建议您先翻阅我之前分享的iOS ...

  4. IOS开发-代码规范

    代码风格的重要性对于一个团队和项目来说不言而喻.网上有许多 Objective-C 的代码风格,但这份简洁而又最符合苹果的规范,同时有助于养成良好的代码习惯,也是我们团队一直遵循的代码风格. 写法没有 ...

  5. iOS开发 代码 或 <Home+Power>截屏

      1. 截屏的两种简单方法, 注意这两种截图方法,都必须在视图完全加载完成后才能截图,即在 viewDidAppear 方法之后截屏,否则无法得到想要的截屏效果 (1) 利用绘图方法 renderI ...

  6. iOS开发代码规范(通用)

    1. 关于命名 1> 统一要求 含义清楚,尽量做到不需要注释也能了解其作用,若做不到,就加注释 使用全称,不适用缩写 2> 类的命名 大驼峰式命名:每个单词的首字母都采用大写字母 例子:M ...

  7. iOS开发代码规范

    1.关于命名 1.1统一要求 含义清楚, 尽量做到不需要注释也能了解其作用,若做不到,就加注释 使用全称不使用缩写 1.2类的命名 大驼峰式命名:每一个单词的首字母都采用大写字母例子: MFHomeP ...

  8. iOS - 开发代码部分规范

    1. 关于命名 1.1 统一要求 含义清楚,尽量做到不需要注释也能了解其作用,若做不到,就加注释 使用全称,不适用缩写 1.2 类的命名 大驼峰式命名:每个单词的首字母都采用大写字母 例子:MFHom ...

  9. IOS开发代码分享之获取启动画面图片的string

    http://www.jb51.net/article/55309.htm 本代码支持 iPhone 6 以下. 支持 iPhone 及 iPad ? 1 2 3 4 5 6 7 8 9 10 11 ...

  10. [IOS 开发代码]UIImage+Blur 网络图片模糊用法

    UIImage-Helpers 网络图片模糊用法   float quality = .00001f;    float blurred = .5f; NSURL *url = [NSURL URLW ...

随机推荐

  1. 运维人员20道必会iptables面试题

    1.详述iptales工作流程以及规则过滤顺序? iptables过滤的规则顺序是由上至下,若出现相同的匹配规则则遵循由上至下的顺序 2.iptables有几个表以及每个表有几个链? Iptables ...

  2. excel中的数据粘贴不全到plsql中,excel 粘贴后空白,Excel复制粘贴内容不全

    http://zhidao.baidu.com/link?url=pHZQvfWJzI-lQjl4uP86q4GLcpYHu4o-fdjiYegJS0Cy5HEq5oz0YrUye3iHjmv5CJ3 ...

  3. git学习笔记(四)—— 分支管理

    一.创建与合并分支 git branch //查看分支 git branch <name> //创建分支 git checkout <name> //切换分支 git chec ...

  4. C# 用timer做成服务后 timer_Tick () 为什么不执行?

    不能使用 窗体的 Timer,他只能在窗体中使用,服务中无法使用请使用 System.Timers.Timer类 protected override void OnStart(string[] ar ...

  5. bootstrap fileinput 文件上传

    最近因为项目需要研究了下bootstrap fileinput的使用,来记录下这几天的使用心得吧. 前台html页面的代码 <form role="form" id=&quo ...

  6. 安装Tomcat的Apr

    转:http://www.cnblogs.com/littlehb/archive/2013/04/02/2994785.html 安装Tomcat的Apr,提升性能 发现 Tomcat 可以用 Ap ...

  7. HTML页面中显示HTML标签<xmp>

    最近做东西遇到一个HTML页面中显示HTML标签的需求,比如要显示</span> 解决方法如下 HTML页面中显示HTML标签代码,可以使用<xmp>html标签内容</ ...

  8. 【C语言】练习5-8

     题目来源:<The C programming language>中的习题P92  练习5-8: 一个日期转换的问题,把某月某日这种日期表示形式转换为某年中第几天的表示形式,反之亦然.例 ...

  9. apktool 在mac下的使用 -反编译安卓apk文件

    1.下载apktool 点击这里下载 ,里面有两个文件,一个是.jar,一个是自己写的脚本.sh 注:最新的apktool.jar 文件可以点击这里下载 .sh脚本是自写脚本可不用更新最新,下载的ja ...

  10. Linux伙伴算法

    Linux内存管理伙伴算法 伙伴算法 Linux内核内存管理的任务包括: 遵从CPU的MMU(Memory Management Unit)机制 合理.有效.快速地管理内存 实现内存保护机制 实现虚拟 ...