MVC设计模式详解

来源:互联网 发布:cmd建立网络路径 编辑:程序博客网 时间:2024/05/18 03:38

M:model

V:View

C:Controller


1.M用来存取数据,所有和存取数据有关的工作交给他就好了.

下面直接用例子来说明:


#import <UIKit/UIKit.h>

#import "Student.h"


@interface Student : NSObject

@property (nonatomic,retain)NSString *name;

@property (nonatomic,retain)NSString *sex;

@property (nonatomic,retain)NSString *phone;

@property (nonatomic,retain)NSString *photo;

@property (nonatomic,retain)NSString *hobby;


@end

上面定义了若干的属性,如果是一个新手的话,会在.m中写很多的初始化方法,便利构造器等,用来初始化或给属性赋值:

- (instancetype)initWithName:(NSString *)name sex:(NSString *)sex ....

+ (Student *)studentWithName:(NSString *)name sex:(NSString *)sex ....


或者你学的还不错,你会这样写:

- (instancetype)initWithDictionary:(NSDictionary *)dic;

student的属性以键值对的方式放在一个字典里,然后把字典传进来,在方法内部进行赋值.

相比之下这个方法就要比上边的初始化要好得多了(字典的好处就在于,可以将字典内容存储到plist文件当中,实现类似存档读档的功能)

操作方便,还节省时间,是个不错的方法



but...这些都不是我要说的,来看看真正的大神怎么写吧:

如果是我的话(哈哈纯属娱乐~~),我会使用KVC模式(注意,不是KFC).只需要给Model类添加需要的属性就好了,也就是说,用到什么数据,就定义为属性.不需要写初始化方法,只要用KVCok,这样代码简单占资源少.(KVC:每个类的实例都会有KVC的相关方法,创建并初始化一个类的实例,就能用实例调用KVC的方法了,setValue: forKey:之类的方法.KVC方法是间接赋值,和属性的setter不一样,setter是直接赋值.一个类没有setter方法也能用KVC赋值),:

#import "Student.h"


@implementation Student

-(void)dealloc

{

    [_name release];

    [_sex release];

    [_phone release];

    [_photo release];

    [_hobby release];

    [super dealloc];

}

//不重写这个函数的话,如果kv不匹配会崩溃,比如传进来的key有四个而类里有三个key;

//默认的实现是抛出一个异常

-(void)setValue:(id)value forUndefinedKey:(NSString *)key

{

    

}


@end

.m文件中没有定义初始化方法


Student *stu=[[Student alloc] init];

[stu setValuesForKeysWithDictionary:[students objectAtIndex:i]];


如题:student类中没有定义初始化方法,但是用KVC可以对齐进行赋值,省下了很多代码,而且还很简单,高手都这么干,




2.V只用来做和显示有关的事情.是什么数据由你Model决定,怎么显示就是我的事了

一般情况下C在控制V的时候,都会用M对其进行传值.作为一个V,你所需要做的就是让外部使用你的时候尽可能的简单,也就是说,你要尽量将一些复杂重复的代码封装在你的内部,对外提供一个简单的接口,这样,外界使用者不用知道你具体是怎么实现的,只要传入一个参数(数组,字典等)就能完成任务.例如:

//cell的声明(.h)

@interface StudentCell : UITableViewCell

@property (nonatomic,retain)UILabel *nameLaber;//显示姓名的laber,下同

@property (nonatomic,retain)UILabel *sexLaber;

@property (nonatomic,retain)UILabel *phoneLaber;

@property (nonatomic,retain)UIImageView *photoImage;//显示头像


@property (nonatomic,retain)Student *stu;

@end


//cell的实现(.m)

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier

{

    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];

    if (self) {

        //初始化相关属性

        self.photoImage=[[[UIImageView alloc] initWithFrame:CGRectMake(5,2, 95, 96)] autorelease];

        self.nameLaber=[[[UILabel alloc] initWithFrame:CGRectMake(100,2, 200, 30)] autorelease];

        self.sexLaber=[[[UILabel alloc] initWithFrame:CGRectMake(100,34, 200, 30)] autorelease];

        

        self.phoneLaber=[[[UILabel alloc] initWithFrame:CGRectMake(100,68, 200, 30)] autorelease];

        _phoneLaber.numberOfLines=0;

        _phoneLaber.font=[UIFont systemFontOfSize:18];

        _photoImage.contentMode=UIViewContentModeScaleAspectFit;

        [self.contentView addSubview:_photoImage];

        [self.contentView addSubview:_nameLaber];

        [self.contentView addSubview:_sexLaber];

        [self.contentView addSubview:_phoneLaber];

        

    }

    return self;

}


//tableView中创建cell

static NSString *cellIdentifier=@"cell";

Student *student=[_studentsArray objectAtIndex:indexPath.row];


cellIdentifier=student.sex;

StudentCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

if (cell==nil) {

    cell=[[[StudentCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];

}


/*

 在这里,(C)要给创建好的cell(V)传值(M),告诉V,你要他显示什么内容,所以你需要给cell里声明的作为cellsubView的属性赋值.

 一个新手的做法是,直接给每个View的子类赋值.

 :

 cell.nameLaber.text=student.name;

 cell.sexLaber.text=student.sex;

 ...

 ...

 有一个附一个,100个呢?这样是很麻烦的对吧,你创建一个cell,就要写一次.太浪费时间&&空间了.可是,又不能在cell的初始化方法里写,因为那时候你还不知道需要的值是什么.

 */

/*

   ****************************标记

cell.stu=[_studentsArray objectAtIndex:indexPath.row];

 */

return cell;

上面是关于tableView的一段代码,当你创建完毕一个cell,你要对其你行传值,重复的写一段相似的代码显然不理智.下面看看高手会怎么做:

如果我们在cell(View)里定义一个属性,类型为Student(Model),用来存放外界(Controller)传进的值,而属性的setter方法我们按下面这样重写

- (void)setStu:(Student *)stu

{

    self.nameLaber.text=stu.name;

    self.sexLaber.text=stu.sex;

    CGRect rect=[stu.phone boundingRectWithSize:CGSizeMake(200,5000) options:NSStringDrawingUsesLineFragmentOrigin attributes:[NSDictionary dictionaryWithObjectsAndKeys:self.phoneLaber.font,NSFontAttributeName,nil] context:nil];

    self.phoneLaber.frame=CGRectMake(100, 68, 200, rect.size.height);

    self.phoneLaber.text=stu.phone;

    self.photoImage.image=[UIImage imageNamed:stu.photo];

}

这样,在上面传值的部分,我们就可以用标记部分所写的方法了,就一行代码,简单吧~

下面我们看看这段代码是怎么工作的

cell.stu就相当于调用了setter方法,我们将整个stu传进来,而不是在外边一个一个的传进来.

在内部,我们再对相关属性进行赋值,怎么样,是不是很简便?

(其实,许多系统内部的类,都用了这个方法,比如UIWindousetRootViewController方法,实际上就在内部调用了[self addSubview:rootViewController.view]方法)


3.C用来做和控制有关的一切事情,目的是为了去耦.

试想,如果一个View中的所有姿势图的事件都由他自己去实现,那么这个类就相当于废掉了,因为你的类只能在你自己的这个项目里用,换到别的地方就不行了.

面向对象的思想是什么?提高代码的复用性.目标是什么?高内聚低耦合.

所谓高内聚,就是指,相同的功能实现代码要紧密的结合(在党的周围0.0哈哈...)

低耦合,就是说,你的类要尽可能的对你的其他类没有依赖性,也就是说,你的类的使用,不依赖于其他的类.

举个例子,你实现了一个你自己定义的UIView的子类,但是你的方法都写在了类的内部,你的View中的buttontitle是登陆,不能改.button的点击事件固定为跳转到你定义好的另一个界面(比如京东的商品界面).那么下次如果你要做一个淘宝的软件,这个登陆界面就不能复用了,因为你的这个界面的button只能跳转到京东的商品界面,所以你还要重写一个登陆界面.

如果此时你把button的事件处理交给一个Controller,让他去实现事件处理,让他去决定点击之后你需要做什么,这就好办了.下次我写别的程序的时候,我就可以把决定权交给那个Controller.这样就实现了代码的复用.

有关去耦的设计模式有:target...action设计模式,代理设计模式,适配器模式等,有兴趣的朋友可以自行查阅相关资料.


--The end





0 0
原创粉丝点击