【发布时间】:2016-07-17 15:18:44
【问题描述】:
这是我的场景。
我有一个 android 活动,我想在其中抽象我的 I/O 依赖项。依赖关系由这个接口表示(为简洁起见进行了编辑):
public interface ITimeDataServer {
TimeRecord[] get(int userID);
void save(TimeRecord record);
}
我希望我的活动能够调用这些接口方法,并让调用代码提供实现。 (我认为相当标准)。
ITimeDataServer myServer;
int myUserID;
void loadRecords() {
TimeRecord[] records = myServer.get(myUserID);
// etc...
}
我的困难是,如何确保myServer 被设置?
这似乎是一个常见问题,但我找不到干净的解决方案。
我的第一个想法是 myServer 将通过构造函数传入,但 Android 活动并没有真正用构造函数实例化。
我想出了几个解决方案,但它们在某些方面都很糟糕:
令人讨厌的解决方案 1
创建一个静态方法来启动活动类,该类接受一个 ITimeDataServer 参数并将其存储在一个静态变量中,活动可以从中访问它:
private static ITimeDataSource theDataSource;
public static void launch(Activity currentActivity, ITimeDataSource dataSource) {
theDataSource = dataSource;
Intent intent = new Intent(currentActivity, MainActivity.class);
currentActivity.startActivity(intent);
}
这很棘手,因为 (a) 数据源是静态的,并且实际上与实例没有关联,并且 (b) 消费者可以通过标准活动 API 而不是这种静态方法来启动活动,这将导致 NullPointerException。
令人讨厌的解决方案 2
我可以创建一个Provider类,提供一个ITimeDataSource的单例实例,使用前需要调用库初始化:
public class TimeDataSourceProvider {
private static ITimeDataSource myDataSource = null;
public void initialize(ITimeDataSource dataSource) {
myDataSource = dataSource;
}
public ITimeDataSource get() {
if (myDataSource == null)
throw new NullPointerException("TimeDataSourceProvider.initialize() must be called before .get() can be used.");
else
return myDataSource;
}
}
这似乎不那么令人讨厌了,但仍然有点讨厌,因为活动的依赖关系并不明显,并且由于可能有很多启动它的路径,因此很可能其中一些人会忘记调用TimeDataSourceProvider.initialize()。
棘手的解决方案 3
作为 #2 的变体,创建一个静态 IODependencyProvider 类,该类必须使用应用启动时的所有依赖项进行初始化。
public class IODependencyProvider {
static ITimeDataSource myTimeData;
static IScheduleDataSource myScheduleData; // etc
public static void initialize(ITimeDataSource timeData, IScheduleDataSource scheduleData /* etc */) {
myTimeData = timeData;
myScheduleData = scheduleData;
//etc
}
public static ITimeDataSource getTimeData() {
if (myTimeData == null)
throw new NullPointerException("IODependencyProvider.initialize() must be called before the getX() methods can be used.");
else
return myTimeData;
}
// getScheduleData(), etc
}
这似乎优于 #1 和 #2,因为初始化失败会更难偷偷摸摸,但它也会在数据类型之间产生相互依赖关系,否则这些数据类型不需要存在。
...以及该主题的其他变体。
使这些解决方案蹩脚的共同主题:
- 需要使用静态字段将不可序列化的信息传递给活动
- 缺乏强制初始化这些静态字段的能力(以及随后的随意性)
- 无法清楚地识别活动的依赖关系(由于依赖静态数据)
什么是笨拙的 Android 开发者?
【问题讨论】:
-
如果静态 initialize() 方法有一个注释说“必须在应用启动期间调用此方法”,那至少可以缓解第二点。
标签: android android-activity dependency-injection abstraction