首页 > 代码库 > 组件接口(API)设计指南[2]-类接口(class interface)
组件接口(API)设计指南[2]-类接口(class interface)
规则1:使用当前平台的描述用语或构架
一个最常见的API错误设计是使用外来的规则,API属于一个特定的平台和相关开发者生态系统。你不能使用任何其他不同平台的描述用语或构架,这会污染你当前的代码库,并破坏你同伴的工作效率。
在编码前要充分了解你的目标平台和代码规范。例如,在IOS和OSX中,不使用异常机制处理错误。统一命名规则(规则要足够详细,但是也要足够简洁)。
了解什么是协议(protocol),委托(delegate),扩展(category)。始终在你的代码中使用术语。遵守构造函数和析构函数的有关命名方案。遵守本地内存管理规则。
就像在自然语言中词汇和语法是不可分割的一样,你的风格必须要符合指定平台。
规则2: 设计解耦
设计时应该使组件和创建该组件的项目没有关联,如果是一个GUI组件或者视图,那么它应该显示一些默认内容。利用现有的框架作为指导,尽可能让委托协议(delegateprotocols)等交互保持松散耦合,适当的使用通知,并注意良好的设计和命名规则。
要做到这一点,为每个组件创建一个单独的测试项目是一个非常有效的方法。强迫组件只使用自己的API。不要提取不相关的类之间的共同点。
下面让我们来看看类的接口。初始化方法是类接口中最重要的部分之一,它决定用户如何使用你的组件。你的类需要对某些必要配置进行初始化。所以,一个明显的规则如下:
规则3: 所需的初始化条件应该由参数提供
如果有什么必须的初始化设置,不要等待需要它的时候再去提供,而要在需要之前由初始化参数提供。如果初始化没有得到所需的条件则立刻返回空(nil)。
// 需要参数,不能是nil
- (id)initWithDelegate:(id<MGTileMenuDelegate>)theDelegate;
规则4: 允许访问初始化参数
这是上一个规则的延续:切记不要把初始化时用户提供的参数隐藏在模块内部。要让用户可以访问他们,并注意是否可以以某种方式修改(清除、或以其他方式修改)。
//必须通过初始化方法指定
@property (nonatomic, weak, readonly) id<MGTileMenuDelegate>delegate;
这是以上两条规则共有的示例。
规则5: 注释你的头文件
实际上,你不会总是提供独立的文档来说明代码功能。如果你不提供独立的文档,那么你的.h文件(以及演示程序)就是你的文档。他们应该被“适当”的注释,这里所说的适当是指:
1:足够详细,但是篇幅不要太长。要足够简洁。
2:针对专业人员(语言和词汇)。所有假设必须足够安全谨慎。不要胡扯,不要含糊不清。
特别是,你应简要说明参数、属性的功能和默认值。让用户在头文件中很容易检索代码功能,而不需要去看可执行代码部分。
//瓦片背景渐变(default:蓝)
@property (nonatomic) CGGradientReftileGradient;
// default: 5 pixels
@property (nonatomic) NSIntegerselectionBorderWidth;
// default:黑(底)白(顶)渐变
@property (nonatomic) CGGradientRefselectionGradient;
规则6: 集成到运行不多于3行代码
你的类应该设计成只需要很少的代码就可以集成到项目(不包括委托/数据源协议等)。不包含代理(delegate)方法,你的用户应该只使用三行代码即可进行最简单的测试,这些行分别是:
1:实例化
2:基本配置。
3:显示或激活它。
以下是来自MGTileMenu的相关代码演示:
// 实例化.
tileController = [[MGTileMenuControlleralloc] initWithDelegate:self];
// 配置.
tileController.dismissAfterTileActivated = NO; // 使它更容易播放
// 显示.
[tileControllerdisplayMenuCenteredOnPoint:locinView:self.view];
规则7: 一个繁杂的演示程序通常意味着组件功能零碎
另一个推论: 你的演示程序大小决定了组件的质量,演示程序越小越好。演示所需代码应该尽可能的少而简单(但是也要在demo中适当的演示组件的定制化服务或功能)。
使用一个空的Xcode模版,应该只需要添加很少的代码,就能够创建具有你的组件核心功能的应用。仅仅提供一个演示程序是不够的,还必须确保复制-粘贴你提供的示例程序后同样能够工作。
规则8: 设置预定方案
我开发应用程序的标准规则是不给用户提供配置选项,而是选择合理的默认值来适用大部分用户。好的软件,总是会固执己见。
关于这一点,组件和应用程序有所不同,因为组件的使用场景是不明确的。当然,你可以让组件只适用于特定的某一种情况,但通常我们更希望组件具有一定的灵活性。你永远不知道其他开发者会如何使用你的组件,因此,你必须让它具有一定的通用性。
仔细考虑组件所支持的功能是很重要的。尤其要考虑依赖关系-不是在编译/链接时的依赖,而是逻辑功能与接口之间的依赖。我的做法是不在实例变量(成员)的层次去想这个问题,而是从“定制方案”的层次去想这个问题。弄清楚你想让你的组件具备哪些定制方案?然后根据这些定制方案,来选择公开哪些属性给使用者。
一个很容易犯的错误是公开的配置项(接口)不足而削弱了某种功能,比如下面的一些例子:
1:公开了宽度和高度设置,但是没有考虑圆角。
2:公开了背景颜色的设置,但是没有考虑高亮时的背景色设置。
3:公开了尺寸设置,但是没有考虑间距。
这种错误通常取决于具体的组件,试着从显示效果和功能的角度考虑属性之间的关系。从用户的角度来考虑问题。要灵活易用,但不要放弃组件的特性。
// 解除菜单后瓦片被激活(YES; 默认)
@property (nonatomic) BOOLdismissAfterTileActivated;
// 右手模式(YES; 默认) 或左手模式(NO)
@property (nonatomic) BOOLrightHanded;
// 每个瓦片的宽高,单位:像素(默认72 像素)
@property (nonatomic) NSIntegertileSide;
// 瓦片间距, 单位:像素(默认: 20 像素)
@property (nonatomic) NSIntegertileGap;
// 瓷砖圆角的半径, 单位:像素(默认: 12.0 像素)
@property (nonatomic) CGFloatcornerRadius;
让常识指引你。找到哪些在70%的情况下都会使用的选项,并提供这些选项给用户。
规则9: 更多的特性,更少的操作
在我喜欢的组件中(其中一些是标准框架,一些是来自第三方的开源组件,也有我自己写的),有一个经常出现的特性。组件所实现的功能(所有功能,从初始化到状态更新等)和公开的接口(存取器或自定义方法等)存在一定的比例关系。
这些组件几乎总是用更少的操作(这不是指界面上的操作,而是接口)来实现更多的特性。MGTileMenu有一个初始化和四个功能方法(其中一个是另一个接口的简化),以它的特性和操作来说,是四倍的比例关系。我认为这是一个不错的比例,在具有简洁实用的功能的同时,也能灵活的定制。
// 参数不能为nil
- (id)initWithDelegate:(id<MGTileMenuDelegate>)theDelegate;
// 从0开始
- (CGPoint)displayMenuPage:(NSInteger)pageNumcenteredOnPoint:(CGPoint)centerPtinView:(UIView *)parentView;
- (void)dismissMenu;
// 从0开始
- (void)switchToPage:(NSInteger)pageNum;
规则10: 在你的控件中使用控件
一个简化API的很好的方法是:在你的组件中使用现有的组件。组件风格统一并不意味着你不能利用现有组件来构造新的组件(实际上这是一个良好的软件工程基本原则)。
例如UITableViewCell和UIButton的API如此简单,就是因为它们使用了子控件UILabels和UIImageViews。如果合适,你也可以这样做-并且暴露子控件,以便让你当前的类接口更加简洁一致。
例如MGTileMenu中,瓦片是由UIButtons扩展的。与在自定义视图中绘制并跟踪输入、提供访问接口相比,这简化了很多工作。
规则11: 你的便利方法也可能适合用户
你一定会在创作组件期间添加一些便利的方法,而又本能的将它们设为私有(private)。其实,你可以考虑使用者在集成你的组件时是否会用到这些方法,以此来决定是否公开(public)它们。
例如,我在MGTileMenu中创建了这些便利的功能:
CGRectMGMinimallyOverlapRects(CGRectinner, CGRectouter, CGFloatpadding);
//RGB色彩渐变
CGGradientRefMGCreateGradientWithColors(UIColor *topColorRGB, UIColor *bottomColorRGB);
第一个是存储位置的结构,用于移动菜单,以便它在其父视图中可以完全可见(方便其他开发人员创建与他们自己的UI配套的菜单)。第二个是存储渐变色的结构,用于为菜单背景设置的图形渐变(当背景改变后,会通过委托通知调用者)。
规则12: 神奇的效果是好的,神奇的数字不是
你迟早会在组件中放入一些奇特的东西。并希望赋予更多像史蒂夫·乔布斯式的令人愉快的神奇特性。但我要说的是在你的代码中具有特殊含义的数字。一个最常见的例子就是-1,它通常用来表示一组特别的东西,或特殊情况。
用一个特定值表示一类情况,这样做很正确。那么什么情况是不正确的那?显然,不能在你的代码中直接使用这种神秘的原始数值,尤其是不能把它暴露在API中。如果你必须暴露它,请把它包装一下,比如使用#defines定义一个可以让人理解的名字。
组件接口(API)设计指南[2]-类接口(class interface)