九宫格之模型,封装初体验

来源:互联网 发布:淘宝网上买东西脏不脏 编辑:程序博客网 时间:2024/06/13 11:41

曾经以为面向对象开发,无非就是封装成类嘛,没什么不同,但是今天在做这个小程序的时候,稍微体会到了一点所谓的,面向对象开发.

以前写C的程序,往往是顺序而过程的编程.像融入到代码中一样,不需要太多的思想.

然而在面向对象编程的时候,需要站在上帝视角上,俯瞰整个项目,着手一个对象模型的构造,在不断的构造对象的时候,实现封装.


===============

用纯代码的方式,实现这样一个小程序



第一步,图标放到Images.xcassets文件中就行了, 因为文件不是太大,不会影响内存的使用, 然后将图片的描述信息,放到Plist文件中,这也相当于实现了一次小封装.


将这些资源准备好, 就可以进行读取了.

从plist文件中就能看出,我们代码中是用一个NSArray 数组接收plist文件中的信息的.

@property (nonatomic, strong) NSArray *appInfos;


再为setter设计功能的时候有不同的方式,也体现了不同程度的封装.

1.我们的目的是从文件中读取相应的控件描述信息给appInfo, 那么我们就直接一点,直接将文件里面读取到的内容放到appinfo中咯

 NSBundle *bundle = [NSBundle mainBundle];            NSString *path = [bundle pathForResource:@"app" ofType:@"plist"];                        _appInfos = [NSArray arrayWithContentsOfFile: path];


这样就OK了,但是这样在后续使用的时候, 很依赖该数组中具体封装的属性  "icon" , "name"  如果后期我在添加其他的属性,或者修改, 那么就需要到源码中不断修改.

        因为我们需要不断使用 数组和字典的语法  给控件赋值 而他们的语法明显很依赖 属性


我们应该始终秉承着这样一个原则,我们所写的主代码就是客户, 而我们图片啊,控件啊,描述信息啊就是我们商品, 任由我们的商品配方如何变化,客户要做的就是消费我们的商品

而这具体消费也是我们提供给客户对这件商品的使用方法, 比如对这件商品 吃,喝, 踹等....当我们的配方变化了的时候,这一系列消费动作还是不变的.

比如,我们给客户提供了,对文件的读取,那么我们就仅仅 一个读取接口,对于接口中配方的变化,我们不让客户知道,客户就知道读取OK了行了.


2. 我们把这几个属性进行封装,封装成一个类,这样做,我们至少以可以使用 点语法,当然这样和上面没有太大的区别,还是有很强的依赖性

也就是说我们让

@property (nonatomic, strong) NSArray *appInfos;
中的数组中存放的是对象,而不是简单几个属性.

首先创建类文件


3. 在将文件中的信息封装成类后, 仍然没有解决过度依赖的问题, 那么我们在类中定义类方法,这样我们在为数组加载数据的时候,只需要调用类方法,让类方法返回读取到的数据即可.

- (instancetype) initWithDic: (NSDictionary *) dic{    if(self = [self init]){        self.name = dic[@"name"];        self.icon = dic[@"icon"];    }    return  self;}+ (instancetype) appInfoWithDic: (NSDictionary *) dic{    return [[self alloc] initWithDic: dic];}+ (NSArray *) appINfoList{        NSBundle *bundle = [NSBundle mainBundle];       NSString *path = [bundle pathForResource:@"app" ofType:@"plist"];        NSArray *dicArray = [NSArray arrayWithContentsOfFile:path];        NSMutableArray *tmpArray = [NSMutableArray array];    // 字典转换模型    for (NSDictionary *dic in dicArray) {               CZAppinfo *appInfo = [CZAppinfo appInfoWithDic: dic];                [tmpArray addObject:appInfo];    }        return tmpArray;}

  这样我们的setter方法实现了低耦合, 高内聚性,

- (NSArray *) appInfos{                   if (_appInfos == nil) {                        _appInfos = [CZAppinfo appINfoList];                    }    return _appInfos;}


=========================

好了第一部分完成了.

下一步看一下控件的方面. 对于控件方面,有两种方式,第一种每个控件单独布局,并计算坐标点.


这个控件分为三部分, 图片控件, label控件还有一个button按钮控件

那么三个控件相对与屏幕的坐标需要挨个计算这很麻烦,我们可以先生成一个基于view的子控件,然后将三个控件在添加到该子控件中,这样他们的坐标就是基于该子控件了

UIView * subView = [[UIView alloc] init];[self.view addSubview: subView];



我们在取得了appInfos中一个对象后,就取得了整个控件的描述信息,只要再写一个方法,去分别生成三个子控件,并为该方法传递一个描述控件的对象.

由于我们对描述信息就行了封装,所以就不需要传递依赖性高的字典了.


首先写一个取数组中描述信息的对象的方法, 然后在该方法中调用子控件生成的方法

- (void) newBox{

CZAppinfo * appinfo = 取一个对象

设置先subView的坐标

传递参数

- (void) addSubView: (UIView *) subView appdesc: (CZAppinfo *) appinfo{

//不要忘记将三个子控件添加到subView上

[subView addSubview: 控件名字_View]; //每个控件都需要添加

}

}

这样就完成了整个界面的布局.


还有一种更简洁的方式去完成这个过程,就是利用 xib布局

创建xib文件



可以看出来, 我们把这三个控件封装到一起,  最外层是一个view控件, 这个过程也需要将三个控件添加为view子控件, 最后为view创建实现类,就像main.storyboard

实现UIView类



那xib中的view实现CZAppinfoView类(自定义控件类)



这样我们就可以在实现类中定义控件属性, 方法 并能创建基于该类的控件,  基于该类创建出来的控件就是上面我们自己自定义的控件了.


- (void) newBox{

CZAppinfo * appinfo = 取一个对象

创建自定类的控件, 设置坐标.

}


为了提高独立性,我们再在CZAppInfoView类中添加一个属性,用于描述各控件信息,这样我们可以随时使用这些信息,因为他们和控件都是类的一部分

@class CZAppinfo;@interface CZAppInfoView : UIView@property (weak, nonatomic) IBOutlet UIImageView *iconView;- (IBAction)loadDown:(UIButton *)sender;@property (weak, nonatomic) IBOutlet UILabel *nameView;@property (nonatomic, strong) CZAppinfo * appInfo; //每个控件都会由相应的描述信息,而不依赖控件本身+(instancetype) loadCZappinfoView;@end


这里有一点需要注意, 我们在创建该类对象时候,其实是从该xib文件中读取对象控件的,我们需要给它实现下相应的读取方法


+(instancetype) loadCZappinfoView{            NSBundle * bundle = [NSBundle mainBundle];            //取出来的是整个组合起来的控件            CZAppInfoView * subView = [[bundle loadNibNamed: @"CZAppInfoView"  owner: nil options: nil] lastObject];<span style="white-space:pre"></span>                     <span style="white-space:pre"></span>    return subView;}

可以看到

            CZAppInfoView * subView = [[bundle loadNibNamed: @"CZAppInfoView"  owner: nil options: nil] lastObject];

我们看下这个方法的定义


这行代码 和我们平时创建对象时不同,我们是使用 loadNibNamed去读取的. 而且从xib中读取的控件组合是一个数组的形式存放的(三个控件为一个元素) 

而我们需要的是整个控件,所以我们用lastObject返回这整个控件(也可以用下标, 里面就一个元素).  一定要注意这个生成过程.

还有不要忘记了,我们在该类中还有一个属性

@property (nonatomic, strong) CZAppinfo * appInfo; //每个控件都会由相应的描述信息,而不依赖控件本身

我们还需要为该类中的appInfo控件描述属性 重写setter方法, 这样当我们从- (void) newBox; 方法中取得数组中的描述信息的对象后, 可以直接为CZAppInfoView所生成的

控件, 添加描述信息.

- (void) setAppInfo:(CZAppinfo *)appInfo{    _appInfo = appInfo;    self.nameView.text  = appInfo.name;    self.iconView.image = [UIImage imageNamed: appInfo.icon];    }

完整的创建控件主代码部分

- (void) newBox{    CGFloat subViewW = 100;    CGFloat subViewH = 100;    CGFloat marginX = (self.view.frame.size.width - 3 * subViewW) / 4;    CGFloat marginY = 20;    for(int i  = 0; i < self.appInfos.count; i ++){        int row = i / 3;        int column = i % 3;                CGFloat subViewX = marginX + column * (marginX + subViewW);        CGFloat subViewY = 30 + row * (marginY + subViewH);                       CZAppInfoView * subView = [CZAppInfoView loadCZappinfoView]; //高度封装后,创建从xib文件中的控件         [self.view addSubview: subView];        subView.frame = CGRectMake(subViewX, subViewY, subViewW, subViewH);                CZAppinfo * appinfo = self.appInfos[i];        subView.appInfo = appinfo;   //通过简单的setter方法完成描述信息的录入            }}



================================================================

做这个小程序,其实学到更多的是 封装的思想, 这远远大于程序本身.

1.在对plist文件中的字典 进行封装成对象

2.对xib文件中的控件组合 封装成对象.

3.对一系列依赖度过高的代码,独立封装成方法.













0 0
原创粉丝点击