再会。
任务:有一个适用于桌面和安卓的应用程序。您需要定义一种将应用程序数据保存到永久内存的架构方法,同时考虑以下特性:
- 某种类型的对象的部分字段是其内部实现的一部分,必须对客户端代码完全隐藏,同时这些字段的值也必须保存。
- 存储对象的位置和方法可以在应用程序的移植和开发过程中更改和添加(因此可以使用 SAX 或 DOM 解析器 [Desctop 版本] 将对象保存在存储在用户机器上的 json 或 xml 文件中;保存在关系[SQLite, HyperSql, MySql] 通过 JDBC 或一些 ORM 连接;保存到服务器)。
- 某些类型的对象是不可变的。
我尝试了哪些方法:
为每个需要保存对象的类实现 save() 和 load() 方法。如果您需要以多种方式保存对象,那么对于每个方法,我们都会创建自己的一对方法(例如,saveToJson()、loadToJson() 等)
我认为的优点:内部实现的字段对客户端代码完全隐藏,同时我们可以轻松保存/加载它们。
我认为缺点:在我看来,单一责任原则被违反了。第二个缺点是这种方法不能应用于不可变字段(因此,不可变对象),因为它们的初始化只能在调用构造函数时执行(我想将所有应用程序对象保存在统一的方式)。
使用稍微修改的 Memento 模式(为了保存一个对象,我们获取它的存储对象并保存它。与该模式的经典实现不同,要恢复创建者对象,我们会将存储对象传递给构造函数,而不是特殊的方法)。
我认为的优点:直接从对象本身读取/写入隐藏字段的值 - 它不起作用。第二个优点是负责保存的代码可以移动到单独的模块中并放在单独的包中。
我认为缺点:保管人对象必须不仅为创建者对象开放对其状态的访问,而且还为将保存它的对象开放。原来,通过保管人对象,创建者对象间接揭示了它的整个状态。
在单独的模块中,为要保存的每种类型的对象声明一个 DAO 接口。如果一个类具有不希望提供对客户端代码的访问的字段,则 DAO 接口被实现为嵌套类。
我认为的优点:由于嵌套类,内部实现的字段对客户端代码完全隐藏。
我认为缺点:由于嵌套类,负责保存的代码不能完全分离到单独的模块中并放在单独的包中。因此,负责保存的代码被模块和包分开。
问题: 通常如何解决类似的问题?我应该选择我考虑的三个选项之一,如果是,为什么?如果您认为我在描述我考虑的一些解决方案的优缺点时犯了错误,请指出。
一开始,我尝试使用 save() 和 load() 方法来完善这个想法。我决定创建一些通用的 Saver 和 Loader 接口,通过它们可以以统一的方式使用 JSON、XML 和 SQL。保存的对象必须具有 save(Saver saver) 方法和带有 Loader 参数的构造函数(不是 load(Loader loader) 方法,而是能够加载最终字段的构造函数)。为了使接口不需要泄露存储对象的隐藏字段,并且可以用于处理任何类型的对象,并且还可以处理 XML、JSON 和 SQL,有必要立即计算它们以处理分层数据结构。我决定做类似的事情:
并下载
但是尝试选择通用接口要么导致接口功能极差,不允许使用数据存储格式(XML、JSON、关系数据库表)的特性,要么变得非常不一致和臃肿。此外,根本不可能为所有处理数据的方式选择一个通用的接口(我无法想象接口应该如何与 ORM、XML 和 JSON 一起使用)。结论——试图为世界上的一切建立一个通用接口——一个最初注定要失败的想法。每种数据存储格式都需要以特定方式处理。
因此,我选择了第二种解决问题的方法(Memento 模式),因为它允许您以特定的方式处理每种数据存储格式(不会将所有内容混为一团),以及完全分离负责从业务逻辑代码中保存的代码,尽管以削弱封装为代价。