-背景-
因为最早是搞NOI什么的,所以没有接触过任何关于软件设计的知识,所以更不懂什么设计典范,标准什么的。
(我记得我刚开始搞竞赛的时候代码是左对齐的。每次写一会代码就要数数现在是第几个begin end。编一段程序开始时先定义一遍a b c d... z变量,不够就aa bb... 搞得和excel一样。逃
到了大学,加入了学校的一个以移动开发为方向的工作室(大爱!)。然后遇到了带我入门学长!(超级感谢他!他超超超牛逼!)然后他每周都会给我讲讲设计规范、项目什么的,一讲就是几个小时,有时候我都累了,他还乐此不疲的讲讲讲。从此就开启了我漫长的OC道路。
(对了,也是他叫我写写技术博客的。就当锻炼我的文笔了(捂脸
-正文-
我们先来看一下维基百科上对面向对象的定义
面向对象程序设计(英语:Object-oriented programming,缩写:OOP)是种具有对象概念的程序编程范型,同时也是一种程序开发的抽象方针。它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关连的数据。在面向对象程序编程里,计算机程序会被设计成彼此相关的对象。
做过算法竞赛的人都知道,一个好的代码一般都是一个函数一个函数堆出来的,而且恨不得main函数中就只有一堆函数名。那时还不知道这是面向过程编程,以为编程只有这种形式。
面向过程是先分析过程,然后再把这些过程一个一个写成函数调用,数据为函数服务。就像有的人像做数学题一样,第一步求一些东西,第二步再求一些东西,第三步......最终一步步把题目做出来,每一个函数都是一个很完整的过程,里面会处理一大堆东西,计算一堆东西,然后创建不同的函数来干不同的事情,但是他们都是一步步做下去,数据的作用就是让函数调用的。
但是这有一个问题,在一个项目中,会经常出现要改一些东西,而面向过程这种设计模式改起来要很麻烦,要改一堆东西,很难去维护一个项目。
于是很自然的就引出了面向对象的概念,以一个对象为中心,各种函数为它服务,它是按照功能来进行划分的。一个大的机器被分成很多部分,然后每个部分都是一个对象,有自己的构成元素和方法。这样的话,以后要改哪个部分,就只要改那个部分了,不用把整个都改个遍。
当然缺点也还是有的(为了OOP而OOP算不算。。)
总结了知乎大佬Milo Yip的话,就是以下几个观点:
现在的OOP有可能不缓存友好(cache friendly)
- 过度封装:使用OOP时,会把一些复杂的问题拆分抽象成为较简单的独立对象,通过对象的互相调用去实现方案。但是,由于对象包含自己封装的数据,一个问题的数据集会被分散在不同的内存区域。互相调用时很可能会出现数据cache miss的情况。
- 多态:一般多态实现中,会使用到虚函数表。虚函数表是通过加入一次间接来实现动态派送。但在调用的时候需要读取虚函数表,增加cache miss的情况。但如果类的数目极多,把函数指针如果和数据放在一起有时候可放缓问题。
- 数据分布:虽然OOP本身并无限制数据的布局方式,但是基本上绝大部分OOP语言都是把成员变量连续包裹在一段内存中。(甚至连C都要放在一个struct中)在OOP中,通过封装,一个类的各种功能会被实现为多个成员函数,而每个成员函数可能只会存取极少量的成员变量。于是存储其中几个成员变量时,其它成员变量也会载入缓存造成浪费。在传统的OOP编程范式及实现方法,数据布局的问题几乎没法解决。
简单的举个面向对象的例子,比如,现在我要获得水饺这个对象(最近最佳团日刚组织了“包饺子”活动 虽然我因为要建模没去...)。
饺子有两个东西:饺子皮和饺子馅,于是我们创造了这两个对象
- 饺子皮:
#import <Foundation/Foundation.h>
@interface DumplingWrapper : NSObject
{
//构成的元素(自身拥有的一些东西
NSNumber *whiteLevel; //1. 雪白程度(这。算一个参数吧
NSNumber *radius; //2. 半径
NSNumber *tasteLevel; //3. 口感
}
//方法(对自己的属性的一些操作
- (NSNumber *)whiteLevel; //1. 获取饺子皮雪白程度(get方法
- (void)setWhiteLevel:(NSNumber *)myWhiteLevel; //2. 设置饺子皮雪白程度(set方法
//以下是各种set、get方法
- (NSNumber *)radius;
- (void)setRadius:(NSNumber *)myRadius;
- (NSNumber *)tasteLevel;
- (void)setTasteLevel:(NSNumber *)myTasteLevel;
- (void)touch; //3. 摸(不是膜)一下这张皮(不要和我说你拿到饺子皮 不会摸一下!
@end
尴尬。我真的不知道能对饺子皮做些什么。除了摸一下。然后下面是方法的具体实现
#import "DumplingWrapper.h"
@interface DumplingWrapper()
@end
@implementation DumplingWrapper
- (instancetype)init {
self = [super init];
if (self) {
whiteLevel = [[NSNumber alloc] initWithInt:5];
radius = [[NSNumber alloc] initWithFloat:4.0];
tasteLevel = [[NSNumber alloc] initWithInt:6];
}
return self;
}
- (void)touch {
NSLog(@"%@",@"你碰了一下他");
}
- (NSNumber *)whiteLevel {
return whiteLevel;
}
- (void)setWhiteLevel:(NSNumber *)myWhiteLevel {
whiteLevel = myWhiteLevel;
}
- (NSNumber *)radius {
return radius;
}
- (void)setRadius:(NSNumber *)myRadius {
radius = myRadius;
}
- (NSNumber *)tasteLevel {
return tasteLevel;
}
- (void)setTasteLevel:(NSNumber *)myTasteLevel {
tasteLevel = myTasteLevel;
}
@end
- 饺子馅:
#import <Foundation/Foundation.h>
@interface DumplingStuffing : NSObject
{
//构成的元素
NSArray<NSString *> *stuffing; //1. 有什么馅料
NSNumber *mass;//2. 馅料的质量
}
//方法
//一堆set、get方法
- (NSArray *)stuffing;
- (void)setStuffing:(NSArray *)myStuffing;
- (NSNumber *)mass;
- (void)setMass:(NSNumber *)myMass;
- (void)addMass:(NSNumber *)addedMass; //1. 增加点馅料
- (void)chopStuffing; //2. 把馅料剁剁剁剁剁
@end
方法的具体实现是
#import "DumplingStuffing.h"
@interface DumplingStuffing()
@end
@implementation DumplingStuffing
- (instancetype)init {
self = [super init];
if (self) {
stuffing = [[NSArray alloc] initWithObjects:@"荠菜",@"猪肉",@"姜", nil];
mass = [[NSNumber alloc] initWithFloat:100];
}
return self;
}
- (void)chopStuffing {
NSLog(@"%@",@"馅料已经被你剁的粉碎");
}
- (NSArray *)stuffing {
return stuffing;
}
- (void)setStuffing:(NSArray *)myStuffing {
stuffing = myStuffing;
}
- (NSNumber *)mass {
return mass;
}
- (void)setMass:(NSNumber *)myMass {
mass = myMass;
}
- (void)addMass:(NSNumber *)addedMass {
mass = [NSNumber numberWithFloat:[mass floatValue] + [addedMass floatValue]];
}
@end
然后饺子自己本身也是一个对象,它拥有饺子皮和饺子馅这两个对象,当然它也有一些它自己的构成元素和方法
#import <Foundation/Foundation.h>
#import "DumplingWrapper.h"
#import "DumplingStuffing.h"
@interface Dumpling : NSObject
{
//构成的元素
DumplingWrapper *dumplingWrapper; //1. 饺子皮
DumplingStuffing *dumplingStuffing; //2. 饺子馅
NSNumber *faceScore; //3. 饺子的颜值
}
//方法
//还是一堆get、set方法(是不是很麻烦,其实可以@property来代替,但是第一次还是写的详细一点
- (DumplingWrapper *)dumplingWrapper;
- (void)setDumplingWrapper:(DumplingWrapper *)myDumplingWrapper;
- (DumplingStuffing *)dumplingStuffing;
- (void)setDumplingStuffing:(DumplingStuffing *)myDumplingStuffing;
- (void)makeDumpling; //1. 包饺子
- (void)cookDumpling; //2. 煮饺子
@end
下面是方法的具体实现
#import "Dumpling.h"
@interface Dumpling()
@end
@implementation Dumpling
- (instancetype)init {
self = [super init];
if (self) {
dumplingStuffing = [[DumplingStuffing alloc] init];
dumplingWrapper = [[DumplingWrapper alloc] init];
faceScore = [[NSNumber alloc] initWithFloat:10];
}
return self;
}
- (void)makeDumpling {
NSLog(@"%@", @"哇!你包了一个饺子");
}
- (void)cookDumpling {
NSLog(@"%@", @"哇!你煮了一个饺子");
}
- (DumplingWrapper *)dumplingWrapper {
return dumplingWrapper;
}
- (void)setDumplingWrapper:(DumplingWrapper *)myDumplingWrapper {
dumplingWrapper = myDumplingWrapper;
}
- (DumplingStuffing *)dumplingStuffing {
return dumplingStuffing;
}
- (void)setDumplingStuffing:(DumplingStuffing *)myDumplingStuffing {
dumplingStuffing = myDumplingStuffing;
}
@end
对象Dumpling和对象DumplingWrapper和DumplingStuffing的关系是“我拥有你”,也就是复合关系,一般处理类和对象关系的时候,最重要的右两个复合和继承,复合的思想就是“我拥有你”,继承的思想是“我是你”。这个也不是这篇文章讨论的重点,有兴趣的小伙伴可以点击下面几个链接看具体的说明 继承(维基百科) 继承(百度百科)
诶。复合这个概念竟然没有单独的词条。那我就给一个《Objective-C基础教程(第二版) Scott Knaster著》上的定义:
将多个组件组合在一起,配合使用,从而得到完整的作品。
其实从上面的例子看的出来,在OOP中,关注的重点已经从函数编程数据了。数据间通过间接方式来引用代码,这些代码可以对数据进行操作。现在煮个饺子什么的都不是叫外面的人来做了,Dumpling的实例可以自己调用自己的cookDumpling来煮,也就是说饺子自己煮!是不是很有意思!
面向对象并不是面向过程的升级版,它们之间都各自有优点缺点,这要看干的事情是啥。
-闲话-
由于首次写这种技术文章,用的都是零碎时间写的,可能写的有点匆忙,有错的地方欢迎指出!此文中的很多概念并没有给出明确定义,因为写的是一些我能想到的关于面向对象的东西,并不是面向对象的详细教程。不过不用@property真的写的很难受(逃
网友评论