博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OC 设计模式
阅读量:5843 次
发布时间:2019-06-18

本文共 14669 字,大约阅读时间需要 48 分钟。

         设计模式

         一种或几种被所有程序员广泛认同的,有某些特定功能,或者实现某些特殊作用的编码格式

         单例模式

         键值编码(KVC)

         键值观察(KVO)

         观察者模式()

         工厂模式(工厂方法)

         ps:MVC && MVVM && CVC

 

一、单例模式

1 #import 
2 3 /* 4 单例模式 5 1.通常情况下,一个工程只有一个单例类。 6 2.我们将一个类选中,将它作为单例类。 7 3.在整个工程中这个类所创建的对象是唯一并且不能被释放 8 9 作用:界面传值-在整个工程任意位置对单例对象的属性进行赋值10 也可以在整个工程的任意位置对单例对象的属性进行访问11 */12 @interface Globle : NSObject13 14 @property (strong, nonatomic) NSString *userId;15 16 // 单例方法17 // 作用:获取唯一单例对象18 + (Globle*) shareGloble;//getGloble19 20 @end21 22 #import "Globle.h"23 24 // 创建一个单例对象,用静态修饰符将这个对象的生命周期延长至整个工程25 static Globle *globle = nil;26 27 @implementation Globle28 29 +(Globle *)shareGloble30 {31 // 第一次执行单例方法,将单例对象初始化32 if (globle == nil) {33 globle = [[Globle alloc] init];34 globle.userId = @"";35 }36 // 之后的每次调用,都会直接返回上面的唯一单例对象37 return globle;38 }39 @end40 41 42 #import
43 #import "Globle.h"44 45 @interface People : NSObject46 47 +(void) showUserId;48 49 @end50 51 #import "People.h"52 53 @implementation People54 55 +(void)showUserId56 {57 NSLog(@"userId:%@",[Globle shareGloble].userId);58 }59 @end60 61 62 #import
63 #import "Globle.h"64 #import "People.h"65 66 int main(int argc, const char * argv[]) {67 @autoreleasepool {68 69 [Globle shareGloble].userId = @"1001";70 [People showUserId];71 72 }73 return 0;74 }

 

二、KVC

1 #import 
2 3 /* 4 KVC key-value-coding 5 6 作用:能够无视OC中关于私有的设定,直接通过底层赋值方式对属性赋值(和获取); 7 8 */ 9 @interface KVCClass : NSObject10 11 -(void) showProperty;12 13 @end14 15 #import "KVCClass.h"16 #import "Student.h"17 18 @interface KVCClass ()19 20 @property (strong, nonatomic) NSString *kvcProperty;21 @property (strong, nonatomic) Student *tempStu;22 23 @end24 25 @implementation KVCClass26 27 - (instancetype)init28 {29 self = [super init];30 if (self) {31 self.tempStu =[[Student alloc] init];32 }33 return self;34 }35 36 -(void)showProperty37 {38 NSLog(@"property:%@",self.kvcProperty);39 NSLog(@"property:%@",self.tempStu.name);40 41 }42 @end43 44 45 #import
46 47 @interface Student : NSObject48 49 @property (strong, nonatomic) NSString *name;50 51 @end52 53 #import "Student.h"54 55 @implementation Student56 57 @end58 59 60 #import
61 62 #import "KVCClass.h"63 int main(int argc, const char * argv[]) {64 @autoreleasepool {65 KVCClass *kvc = [[KVCClass alloc] init];66 67 //表示 在KVC对象中查找名字为[kvcProperty] 的属性,并对这个属性赋值为@“bowen”68 // 设置69 [kvc setValue:@"bowen" forKey:@"kvcProperty"];70 71 [kvc setValue:@"bowen1" forKeyPath:@"tempStu.name"];72 73 [kvc showProperty]; // 取值

             NSLog(@"%@",[kvc valueForKey:@"kvcProperty"]);

74         NSLog(@"%@",[kvc valueForKeyPath:@"tempStu.name"]);75     }76     return 0;77 }

 

OC中的KVC操作就和Java中使用反射机制去访问类的private权限的变量,很暴力的,这样做就会破坏类的封装性,本来类中的的private权限就是不希望外界去访问的,但是我们这样去操作,就会反其道而行,但是我们有时候真的需要去这样做,哎。所以说有些事不是都是顺其自然的,而是需要的时候自然就诞生了。

下面就来看一下这种技术的使用:

Dog.h

 

[objc] 
  1. //  
  2. //  Dog.h  
  3. //  42_KVC  
  4. //  
  5. //  Created by jiangwei on 14-10-14.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import <Foundation/Foundation.h>  
  10.   
  11. @interface Dog : NSObject  
  12.   
  13. @end  

 

 

Dog.m

 

[objc] 
  1. //  
  2. //  Dog.m  
  3. //  42_KVC  
  4. //  
  5. //  Created by jiangwei on 14-10-14.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import "Dog.h"  
  10.   
  11. @implementation Dog  
  12.   
  13. @end  

定义了Dog这个类,但是什么都没有,他只是一个中间类,没什么作用,在这个demo中。

 

 

Person.h

 

[objc] 
  1. //  
  2. //  Person.h  
  3. //  42_KVC  
  4. //  
  5. //  Created by jiangwei on 14-10-14.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import <Foundation/Foundation.h>  
  10. #import "Dog.h"  
  11.   
  12. @interface Person : NSObject{  
  13. @private  
  14.     NSString *_name;  
  15.     NSDog *_dog;  
  16.       
  17.     NSInteger *age;  
  18. }  
  19.   
  20. @end  

 

 

Person.m

 

[objc] 
  1. //  
  2. //  Person.m  
  3. //  42_KVC  
  4. //  
  5. //  Created by jiangwei on 14-10-14.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import "Person.h"  
  10.   
  11. @implementation Person  
  12.   
  13. - (NSString *)description{  
  14.     NSLog(@"%@",_name);  
  15.     return _name;  
  16. }  
  17.   
  18. @end  

Person类中我们定义了两个属性,但是这两个属性对外是不可访问的,而且也没有对应的get/set方法。我们也实现了description方法,用于打印结果

 

 

看一下测试代码

main.m

 

[objc] 
  1. //  
  2. //  main.m  
  3. //  42_KVC  
  4. //  
  5. //  Created by jiangwei on 14-10-14.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import <Foundation/Foundation.h>  
  10. #import "Person.h"  
  11. #import "Dog.h"  
  12.   
  13. //KVC:很暴力,及时一个类的属性是私有的,而且也没有get/set方法,同样可以读写  
  14. //相当于Java中的反射,破坏类的封装性  
  15. int main(int argc, const charchar * argv[]) {  
  16.     @autoreleasepool {  
  17.           
  18.         Person *p = [[Person alloc] init];  
  19.           
  20.         //设置值  
  21.         //这里setValue方法:第一个参数是value,第二个参数是key(就是类的属性名称)  
  22.         [p setValue:@"jiangwei" forKey:@"name"];  
  23.           
  24.         Dog *dog = [[Dog alloc] init];  
  25.         [p setValue:dog forKey:@"dog"];  
  26.           
  27.         //KVC设置值时,如果属性有set方法,则优先调用set方法,如果没有则直接设置上去,get方法类似  
  28.           
  29.         //读取值  
  30.         NSString *name = [p valueForKey:@"name"];  
  31.           
  32.         //设置基本数据类型  
  33.         //这里需要将基本类型转化成NSNumber  
  34.         //在设置值的时候,会有自动解包的过程,NSNumber会解包赋值给age  
  35.         [p setValue:@22 forKey:@"age"];  
  36.           
  37.         NSLog(@"%@",p);  
  38.           
  39.         return 0;  
  40.     }  
  41.     return 0;  
  42. }  

这里我们生成一个Person对象,然后开始使用KVC技术了:

 

 

1、设置属性值

 

[java] 
  1. //设置值  
  2. //这里setValue方法:第一个参数是value,第二个参数是key(就是类的属性名称)  
  3. [p setValue:@"jiangwei" forKey:@"name"];  
  4.           
  5. Dog *dog = [[Dog alloc] init];  
  6. [p setValue:dog forKey:@"dog"];  

使用setValue方法,就可以进行对属性进行设置值操作了,同时需要传递这个属性的名称,这个和Java中使用反射机制真的很像。

 

 

注:KVC设置值时,如果属性有set方法,则优先调用set方法,如果没有则直接设置上去,get方法一样

 

[java] 
  1. //设置基本数据类型  
  2. //这里需要将基本类型转化成NSNumber  
  3. //在设置值的时候,会有自动解包的过程,NSNumber会解包赋值给age  
  4. [p setValue:@22 forKey:@"age"];  

还有一个需要注意的地方:当我们在设置基本类型的时候,需要将其转化成NSNumber类型的。

 

2、取属性的值

 

[java] 
  1. //读取值  
  2. NSString *name = [p valueForKey:@"name"];  

取值就简单了

 

 

下面再来看一下KVC中强大的功能:键值路径

键值路径是对于一个类中有数组对象的属性进行便捷操作。

看个场景:

一个作者有多本书

Author.h

 

[objc] 
  1. //  
  2. //  Author.h  
  3. //  43_KeyValuePath  
  4. //  
  5. //  Created by jiangwei on 14-10-15.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import <Foundation/Foundation.h>  
  10.   
  11. @interface Author : NSObject{  
  12.     NSString *_name;  
  13.       
  14.     //作者出版的书,一个作者对应多个书籍对象  
  15.     NSArray *_issueBook;  
  16. }  
  17.   
  18. @end  

 

作者类中定义了名字和一个书籍数组

 

Author.m

 

[objc] 
  1. //  
  2. //  Author.m  
  3. //  43_KeyValuePath  
  4. //  
  5. //  Created by jiangwei on 14-10-15.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import "Author.h"  
  10.   
  11. @implementation Author  
  12.   
  13. @end  

 

 

Book.h

 

[objc] 
  1. //  
  2. //  Book.h  
  3. //  43_KeyValuePath  
  4. //  
  5. //  Created by jiangwei on 14-10-15.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import <Foundation/Foundation.h>  
  10. #import "Author.h"  
  11.   
  12. @interface Book : NSObject{  
  13.     Author *_author;  
  14. }  
  15.   
  16. @property NSString *name;  
  17. @property floatfloat *price;  
  18.   
  19. @end  

定义了一个作者属性,书的名字,价格

 

 

Book.m

 

[objc] 
  1. //  
  2. //  Book.m  
  3. //  43_KeyValuePath  
  4. //  
  5. //  Created by jiangwei on 14-10-15.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import "Book.h"  
  10.   
  11. @implementation Book  
  12.   
  13. @end  

看一下测试代码

 

main.m

 

[objc] 
  1. //  
  2. //  main.m  
  3. //  43_KeyValuePath  
  4. //  
  5. //  Created by jiangwei on 14-10-15.  
  6. //  Copyright (c) 2014年 jiangwei. All rights reserved.  
  7. //  
  8.   
  9. #import <Foundation/Foundation.h>  
  10. #import "Book.h"  
  11. #import "Author.h"  
  12.   
  13. int main(int argc, const charchar * argv[]) {  
  14.     @autoreleasepool {  
  15.           
  16.         //------------------KVC键值路径  
  17.         /* 
  18.         Book *book = [[Book alloc] init]; 
  19.         Author *author = [[Author alloc] init]; 
  20.          
  21.         //设置作者 
  22.         [book setValue:author forKey:@"author"]; 
  23.          
  24.         //设置作者的名字 
  25.         //路径为:author.name,中间用点号进行连接 
  26.         [book setValue:@"jiangwei" forKeyPath:@"author.name"]; 
  27.         NSString *name = [author valueForKey:@"name"]; 
  28.         NSLog(@"name is %@",name); 
  29.          */  
  30.           
  31.           
  32.         //--------------------KVC的运算  
  33.         Author *author = [[Author alloc] init];  
  34.         [author setValue:@"莫言" forKeyPath:@"name"];  
  35.           
  36.         Book *book1 = [[Book alloc] init];  
  37.         book1.name = @"红高粱";  
  38.         book1.price = 9;  
  39.         Book *book2 = [[Book alloc] init];  
  40.         book2.name = @"蛙";  
  41.         book2.price = 10;  
  42.         NSArray *array = [NSArray arrayWithObjects:book1,book2, nil nil];  
  43.         [author setValue:array forKeyPath:@"issueBook"];  
  44.           
  45.         //基本数据类型会自动被包装成NSNumber,装到数组中  
  46.         //得到所有书籍的价格  
  47.         NSArray *priceArray = [author valueForKeyPath:@"issueBook.price"];  
  48.         NSLog(@"%@",priceArray);  
  49.           
  50.         //获取数组的大小  
  51.         NSNumber *count = [author valueForKeyPath:@"issueBook.@count"];  
  52.         NSLog(@"count=%@",count);  
  53.           
  54.         //获取书籍价格的总和  
  55.         NSNumber *sum = [author valueForKeyPath:@"issueBook.@sum.price"];  
  56.         NSLog(@"%@",sum);  
  57.           
  58.         //获取书籍的平均值  
  59.         NSNumber *avg = [author valueForKeyPath:@"issueBook.@avg.price"];  
  60.         NSLog(@"%@",avg);  
  61.           
  62.         //获取书籍的价格最大值和最小值  
  63.         NSNumber *max = [author valueForKeyPath:@"issueBook.@max.price"];  
  64.         NSNumber *min = [author valueForKeyPath:@"issueBook.@min.price"];  
  65.           
  66.     }  
  67.     return 0;  
  68. }  

 

 

1、首先通过前面说到的KVC设置作者的书籍数组

 

[objc] 
  1. //--------------------KVC的运算  
  2. Author *author = [[Author alloc] init];  
  3. [author setValue:@"莫言" forKeyPath:@"name"];  
  4.   
  5. Book *book1 = [[Book alloc] init];  
  6. book1.name = @"红高粱";  
  7. book1.price = 9;  
  8. Book *book2 = [[Book alloc] init];  
  9. book2.name = @"蛙";  
  10. book2.price = 10;  
  11. NSArray *array = [NSArray arrayWithObjects:book1,book2, nil nil];  
  12. [author setValue:array forKeyPath:@"issueBook"];  

添加了两本书籍

 

 

2、下面就开始用到KVC中键值路径了

1)获取作者类中书籍数组中所有书籍的价格

 

[java] 
  1. //基本数据类型会自动被包装成NSNumber,装到数组中  
  2. //得到所有书籍的价格  
  3. NSArray *priceArray = [author valueForKeyPath:@"issueBook.price"];  
  4. NSLog(@"%@",priceArray);  

看到了:@"issueBook.price" 这就是键值路径的使用,issueBook是作者类中的书籍数组属性名,price是书籍类的属性,中间用点号进行连接,这样我们就可以获取到了所有书籍的价格了,如果在Java中,我们需要用一个循环操作。但是OC中多么方便。

 

 

2)获取作者类中书籍数组的大小

 

[java] 
  1. //获取数组的大小  
  2. NSNumber *count = [author valueForKeyPath:@"issueBook.@count"];  
  3. NSLog(@"count=%@",count);  

使用 @"issueBook.@count" 键值路径获取书籍数组的大小,issueBook是作者类中的书籍数组属性名,@count是特定一个写法,可以把它想象成一个方法,中间任然用点号进行连接

 

 

3)获取作者类中书籍数组的价格总和

[java] 
  1. //获取书籍价格的总和  
  2. NSNumber *sum = [author valueForKeyPath:@"issueBook.@sum.price"];  
  3. NSLog(@"%@",sum);  

使用 @"issueBook.@sum.price" 键值路径获取书籍数组中的价格总和,issueBook是作者类中的书籍数组属性名,@sum是特性写法,可以把它想象成一个方法,price是书籍的价格属性名,可以把它看成是@sum的一个参数,中间用点号进行连接

 

如果在java中,这个需要用一个循环来计算总和,OC中很方便的

 

4)获取作者类中书籍数组的价格平均值、最小值、最大值

[objc] 
  1. //获取书籍的平均值  
  2. NSNumber *avg = [author valueForKeyPath:@"issueBook.@avg.price"];  
  3. NSLog(@"%@",avg);  
  4.   
  5. //获取书籍的价格最大值和最小值  
  6. NSNumber *max = [author valueForKeyPath:@"issueBook.@max.price"];  
  7. NSNumber *min = [author valueForKeyPath:@"issueBook.@min.price"];  

 

操作和上面类似,这里就不解释了

 

我们看到上面返回来的数据都是NSNumber类型的

 

 

 

三、观察者模式

1 #import 
2 3 /* 4 观察者模式(通知模式)NSNotification 5 观察者中心 NSNotificationCenter 6 7 注册观察者必需在[发出通知]之前 8 9 */10 11 @interface House : NSObject12 13 -(void) burn;14 15 @end16 17 #import "House.h"18 19 @implementation House20 21 -(void)burn22 {23 NSLog(@"房子着火了");24 // 发出通知25 [[NSNotificationCenter defaultCenter] postNotificationName:@"PlanB" object:nil];26 }27 @end28 29 #import
30 31 @interface Child : NSObject32 33 -(void) loudSay;34 35 -(void) toBeAGuanChazhe;36 37 @end38 39 #import "Child.h"40 41 @implementation Child42 43 -(void) loudSay44 {45 NSLog(@"报告:房子着火了");46 }47 48 -(void)toBeAGuanChazhe49 {50 // 创建观察者51 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loudSay) name:@"PlanB" object:nil];52 }53 @end54 55 #import
56 57 #import "House.h"58 #import "Child.h"59 60 61 int main(int argc, const char * argv[]) {62 @autoreleasepool {63 64 House *myhouse = [[House alloc] init];65 66 Child *mychild = [[Child alloc] init];67 68 // 让mychild 成为观察者69 [mychild toBeAGuanChazhe];70 71 // 让房子着火,发出PlanB现象72 [myhouse burn];73 }74 return 0;75 }

 

四、KVO

1 #import 
2 #import "JieCao.h" 3 4 /* 5 KVO key-value-observer(键值观察) 6 7 作用:能够在被观察的某个属性发生变化的时候,自动执行某些方法 8 9 当被观察的某个属性发生变化的时候,这个方法被自动回调10 11 -(void) observer...12 13 pss:当键值观察的观察者被使用完毕之后必须注销14 psss:键值观察是在[某个类中]给[某个属性] 注册观察者,当这个属性的值发生变化后作出响应模式15 16 */17 18 @interface Bowen : NSObject19 20 @property (strong,nonatomic) JieCao *my_jieCao;21 22 @end23 24 25 #import "Bowen.h"26 27 @implementation Bowen28 29 -(void) setMy_jieCao:(JieCao *)my_jieCao30 {31 _my_jieCao = my_jieCao;32 33 //注册观察者,观察_my_jieCao属性的值的变化34 [_my_jieCao addObserver:self forKeyPath:@"jieCaoValue" options:NSKeyValueObservingOptionNew context:nil];35 36 }37 // 不管在一个类当中注册一个多少KVO的观察者,本方法只能写一个38 -(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context39 {40 if ([keyPath isEqualToString:@"jieCaoValue"]) {41 42 if ([[change objectForKey:@"new"] integerValue]<10) {43 NSLog(@"我要吐了,受不了老师了");44 }45 // NSLog(@"keyPath:%@",keyPath);46 // NSLog(@"Object:%@",keyPath);47 // NSLog(@"change:%@",[change objectForKey:@"new"]);48 49 }50 }51 - (void)dealloc52 {53 [_my_jieCao removeObserver:self forKeyPath:@"jieCaoValue"];54 }55 56 @end57 58 #import
59 60 @interface JieCao : NSObject61 62 @property (assign, nonatomic) NSInteger jieCaoValue;63 64 @end65 66 #import "JieCao.h"67 68 @implementation JieCao69 70 @end71 72 #import
73 74 #import "Bowen.h"75 #import "JieCao.h"76 77 int main(int argc, const char * argv[]) {78 @autoreleasepool {79 // 创建被观察属性80 JieCao *jieCao = [[JieCao alloc] init];81 jieCao.jieCaoValue = 100;82 // 创建观察对象83 Bowen *bowen = [[Bowen alloc] init];84 // 让观察对象开始观察[被观察属性]85 bowen.my_jieCao = jieCao;86 // 让被观察属性发生变化87 bowen.my_jieCao.jieCaoValue = 9;88 }89 return 0;90 }

 

五、工厂模式

1 #import 
2 #import "Fruit.h" 3 4 /* 5 6 工厂模式:(工厂方法) 7 8 个人理解:实际上就是用来快速大批量生产对象的 9 10 优点:能够将原本在本类初始化的对象,延时到子类中去初始化。实现了一个开放封闭原则。 11 12 */ 13 14 @interface FruitFactory : NSObject 15 16 // 通过外部输入的名字,创建[名字对应的类] 的对象 17 + (Fruit *)createFruitByName:(NSString *) name; 18 19 @end 20 21 #import "FruitFactory.h" 22 23 @implementation FruitFactory 24 25 +(Fruit *)createFruitByName:(NSString *)name 26 { 27 //需要知道名字对应的类 28 Class class = NSClassFromString(name); 29 // 创建想要的类对象 30 Fruit *fruit = [[class alloc] init]; 31 // 返回新建的[想要的类]的对象 32 return fruit; 33 } 34 @end 35 36 37 #import
38 39 @interface Fruit : NSObject 40 41 -(void) show; 42 43 @end 44 45 #import "Fruit.h" 46 47 @implementation Fruit 48 49 -(void)show 50 { 51 NSLog(@"I'm fruit"); 52 } 53 @end 54 55 #import "Fruit.h" 56 57 @interface Apple : Fruit 58 59 @end 60 61 62 #import "Apple.h" 63 64 @implementation Apple 65 66 -(void)show 67 { 68 NSLog(@"I'm apple"); 69 } 70 71 72 #import "Fruit.h" 73 74 @interface Banana : Fruit 75 76 @end 77 78 #import "Banana.h" 79 80 @implementation Banana 81 82 -(void)show 83 { 84 NSLog(@"I'm banana"); 85 } 86 87 @end 88 89 #import "Fruit.h" 90 91 @interface Pear : Fruit 92 93 @end 94 95 #import "Pear.h" 96 97 @implementation Pear 98 99 -(void)show100 {101 NSLog(@"I'm pear");102 }103 104 @end105 106 #import
107 #import "FruitFactory.h"108 109 int main(int argc, const char * argv[]) {110 @autoreleasepool {111 Fruit *fruit = [FruitFactory createFruitByName:@"Apple"];112 [fruit show];113 Fruit *fruit1 = [FruitFactory createFruitByName:@"Banana"];114 [fruit1 show];115 Fruit *fruit2 = [FruitFactory createFruitByName:@"Pear"];116 [fruit2 show];117 }118 return 0;119 }

 

转载地址:http://jzqcx.baihongyu.com/

你可能感兴趣的文章
Java java.lang.SecurityException: Prohibited package name
查看>>
tomcat部署教程
查看>>
Internet概念与TCP/ IP分层模型
查看>>
SCP:一个去中心化的文学故事
查看>>
BZOJ4557:[JLOI2016/SHOI2016]侦察守卫——题解
查看>>
C++程序设计(第2版)课后习题答案--第14章
查看>>
docker mysql Exit 1
查看>>
我不信奉Scrum,我信奉敏捷
查看>>
第四章例题、心得及问题。
查看>>
23种设计模式
查看>>
【ASP.NET开发】.NET三层架构简单解析
查看>>
svnsync实现版本库的同步备份
查看>>
第1课:接口测试和jmeter总结
查看>>
Redis命令总结
查看>>
多线程
查看>>
Socket
查看>>
Linux下“/”和“~”的区别
查看>>
复制数据库
查看>>
设计模式相关
查看>>
form——验证器Validators
查看>>