现在的位置: 首页 > 综合 > 正文

Objective-C 的 API 设计(API Design)

2014年06月07日 ⁄ 综合 ⁄ 共 2872字 ⁄ 字号 评论关闭

我最常做的开发任务是设计一个可重用的API组件。组件通常为iOS(尽管有时它们是OS X) 设计的,且总是GUI控件或某种视图。

多年来,我为客户开发了很多API组件,其中包括像Apple这样的客户,而且我已经很了解这个过程。我也定期发布开源组件,并且我把曾经对我有帮助的资料和API设计指南放在一起与大家分享。

这是一个重要的主题,无论你是一个开源贡献者,或作为团队的一员参与开发大型的应用,或者只是设计自己的软件。正如开发一个应用的过程,API接口是使用你代码的开发者对你代码的第一印象,将严重影响着开发者决定是使用或扔掉它。

APIs是开发者的用户体验。我一直惊讶,具体到这个流行平台上没有很多的资料是写我们这方面工作的。

当我们阅读一些设计指南时,必要的时候,我将要用我最近发布的开源GUI组件MGTileMenu作为一个例子。你可以在这里先阅读所有关于MGTileMenu的信息,如果你喜欢。

如何令人满意

应用程序接口(API)设计和用户界面、用户体验设计很相像。你的目标用户有不同的需求和特点,但归根结底他们的目标还是把需求完成而已。就像一个设计友好、易用的应用程序的用户界面一样,你需要让你的API有以下的特点:

  1. 直观性
  2. 容错性
  3. 易用性

如同人们设计的其它的软件一样,我们首先需要考虑的是使用案列。我们的设计需要使最经常被用到的的功能简单易用,不需要过度的配置。在默认配置下软件就应该是可用的,并且具有一定的可配置性。软件的设计应该具有可探索性,而且应该允许用户从已知的的范例中推广到其他应用场景。这和我们创建一个用户界面的规则非常的相像。

开发者的界面

用于和开发者交互的元素使用四个主要的显示意味着:

  1. 类界面:暴露的属性和方法。
  2. 委托规则,相关的
  3. 数据源规则,适当的
  4. 任何可以提供的通知

我们需要把每一个都设计成:明智和慎重的,用于人类使用。这里有2个问题当你设计API的时候需要考虑:

  • 什么是控制?

    这将会影响到界面和便利的方法。这是一个按钮?一个滑动器?你的界面是很明显的。你的便利的方法将会遵循这些标准的语义控制
  • 控制长什么样?

    这影响到委托和/或数据源模型和通知。如果这是一个新类型的控制,这个是不是在基本原则上会和其他东西很像?一个大纲性的概念是一个线性表。一个日期的小工具是一个日期的选择器。在一个同一标准下的命令的集合是一个菜单。

我们的核心原则是让已有的类和模型保持一致性,以用来保证我们可以把一个开发者不熟悉的控制让他很轻松的在他可以理解的平台上使用。使用标准APIs,模型,和模式无论是不是可能(并且这个应该是总用的)。对于终端用户,熟悉和直觉性是和代码层级一样重要的。

让我们看看我们之前提到的这四个元素:

类接口

Here’s the interface file for MGTileMenu.

在我们讨论具体的接口之前,这有一些涵盖范围比较广泛的规则:

Rule 1: 使用方言

我所看到最常见的错误是API的设计利用了外来的约定。APIs 属于固定平台和固定的开发者生态系统。你根本无法使用任何习语和你用过的其他平台的架构,这样做会污染您当前的代码库,并对其他开发人员的效率造成损害。

在coding之前要了解你目标平台的约定,比如,在iOS 或者 OS X,不使用异常对待control的流程 。以适当的方式命名你的方法(通常指有足够详细,但也应该有足够的简洁)。

了解协议,和委托,类别分别是什么。在你的代码中使用他们。学习相关的构造函数和析构函数的命名方案。请遵守内存管理规则。词汇和语法是不可分割的,你要么发展为一个固定的的平台,或者你跨平台。

Rule 2: 设计解耦

任何component的设计应该没有连接到你当前创建的项目,如果他是一个GUI control或者一个视图,它应该默认显示一些东西。使用现有的框架作为一个指南,与委托协议,精心设计的/命名的API方法和通知在适当的地方保持松耦合。

一个很明显的,但非常有效的方式,是每次为你的component创建一个项目,并逐渐的隔离开发component。强迫自己使用自己的API。远离无关的类。

接下来,让我们来适当谈谈类的接口。初始化方法的接口中最重要的部分之一,因为他们是人们如何开始使用您的组件。你的类将有一定的初始配置所需的设置。所以,一个明显的规律:

Rule 3: 必须设置初始化参数

如果有什么需要设置的,不要等待 -需要它了就去做,如果你没有得到的东西的立即返回nil。

1 -
(id)initWithDelegate:(id<MGTileMenuDelegate>)theDelegate; 
//
required parameter; cannot be nil.

Rule 4: 允许访问初始化参数Allow access to initializer parameters

这个前一个结果的必然结果: 记住不要仅仅传入参数,应该可以通过属性或者赋值来访问他们,如果他们可以通过任何方式来一场“按摩”(修改,重写等)

1 @property
(nonatomic, weak, readonly) id<MGTileMenuDelegate> delegate; 
//
must be specified via initializer method.

前两个例子阐述了这个观点。

Rule 5: 注释你的header文件 (包含默认值)

实际上,你不总为component提供单独的文档。如果你不提供文档,你的.h文件(包括demo app)就是你的文档。他们应该适当的描述,我的意思是:

  1. 足以描述,但是不是特别多,要简洁。
  2. 一切是提供给专业人士,所以适当的描述别描述无关的事情。

特别是,你应该简要注释在属性或访问器旁边;头文件扫描比在初始化实例的时候更容易。

1 @property
(nonatomic) CGGradientRef tileGradient; 
//
gradient to apply to tile backgrounds (default: a lovely blue)
2 @property
(nonatomic) NSInteger selectionBorderWidth; 
//
default: 5 pixels
3 @property
(nonatomic) CGGradientRef selectionGradient; 
//
default: a subtle white (top) to grey (bottom) gradient

Rule 6: 习惯于运行3行代码

你的类应该设计成只需要最少的代码来集成(包括后续将用到的委托/数据源协议)。但不包括委托方法,你应该着手于用3行代码就可以达到测试的目的。

这3行代码如下:

  1. 实例化你的类
  2. 基本配置,使之能够展示一些信息
  3. 展示或激活它(类的示例)

就这样。任何实质上更繁杂的就会导致代码坏味(code smell)。下面是 MGTileMenu’s
demo app中
 相关的几行代码:

1 //
Instantiate.
2 tileController
= [[MGTileMenuController alloc] initWithDelegate:self];
3  
4 //
Configure.

抱歉!评论已关闭.