清洁架构:如何在UI

时间:2015-09-26 09:37:31

标签: java android architecture coding-style

我正在尝试根据Android中的Uncle Bob's Clean Architecture进行设计。

问题:

我想解决的是如何使在一个存储库中生成的更改反映在应用程序的其他部分,如其他存储库或视图。

示例

我为这个例子设计了一个非常简单的例子。请注意,边界接口已被删除,以使图表保持较小。

想象一个应用程序,显示一个视频列表(包括标题,thumnail和类似的计数),点击视频,你可以看到详细信息(你可以喜欢/不喜欢视频)。

此外,该应用还有一个统计系统,可以计算用户喜欢或不喜欢的视频数量。

此应用的主要课程可能是:

对于视频部分/模块: enter image description here

对于统计部分/模块: enter image description here

目标

现在想象一下,检查你的统计数据,然后浏览视频列表,打开一个视频的详细信息,然后点击“赞”按钮。

在将类似内容发送到服务器之后,应用程序中有几个应该知道更改的元素:

  • 当然详细视图,应该随更改一起更新(这可以通过回调进行,所以没问题)
  • 视频列表应更新给定视频的“赞”数
  • StatsRepository可能希望在投票新视频后更新/无效缓存
  • 如果统计信息列表可见(想象一下分屏),它还应显示更新的统计信息(或至少接收重新查询数据的事件)

问题

解决此类沟通的常见模式有哪些? 请尽可能完整地回答您的问题,指定事件的生成位置,通过应用程序传播的方式等等。

注意:奖励将用于完成答案

2 个答案:

答案 0 :(得分:12)

发布/订阅

通常,对于n:m通信(n个发送者可以向m个接收者发送消息,而所有发送者和接收者彼此不认识),您将使用publish/subscribe pattern。 有许多库实现了这种通信风格,对于Java,例如有EventBus implementation in the Guava library。 对于应用内通信,这些库通常称为EventBus或EventManager,并发送/接收事件

域事件

假设您现在创建了一个事件VideoRatedEvent,它表示用户喜欢或不喜欢视频。 这些类型的事件称为Domain Events。事件类是一个简单的POJO,可能如下所示:

class VideoRatedEvent {
    /** The video that was rated */
    public Video video;
    /** The user that triggered this event */
    public User user;
    /** True if the user liked the video, false if the user disliked the video */
    public boolean liked;
}

派遣事件

现在,每当您的用户喜欢或不喜欢视频时,您都需要发送VideoRatedEvent。 使用Guava,您只需将实例化的事件对象传递给EventBus.post(myVideoRatedEvent)的对象。 理想情况下,事件在域对象中生成,并在持久化事务中调度(有关详细信息,请参阅this blog post)。 这意味着,当您的域模型状态被持久化时,将调度事件。

事件监听器

在您的应用程序中,受事件影响的所有组件现在都可以侦听域事件。 在您的特定示例中,VideoDetailViewStatsRepository可能是VideoRatedEvent的事件侦听器。 当然,您需要使用EventBus.register(Object)将这些注册到Guava EventBus。

答案 1 :(得分:7)

这是我个人的5点,也许与你的#34; The Clean Architecure"的例子不太相关。
我通常会尝试强制一种MVC on androids活动和片段,并使用发布/订阅进行通信。作为组件,我有处理业务逻辑和数据状态的模型类。它们的数据更改方法只能由控制器类调用,控制器类通常是活动类,并且还处理会话状态。我使用片段来管理应用程序的不同视图部分和这些片段下的视图(显然)。所有片段都订阅一个或多个主题。我使用自己的简单DataDistributionService来处理不同的主题,从注册的发布者那里获取消息并将它们转发给所有订阅者。 (部分受到OMG DDS的影响,但更多原始)一个简单的应用程序只有一个主题,例如"主&#34 ;.

视图交互(触摸等)的每个部分首先由其片段处理。片段可能会在不发送通知的情况下更改一些内容。例如。如果应用程序的其余部分不需要知道/反应,则切换渲染数据元素的子范围。否则,片段将包含必要参数的ViewRequest(...)发布到DDS
DDS广播该消息并在某个时刻到达控制器。这可以只是主要活动或特定控制器实例。应该只有一个控制器,以便请求只处理一次。控制器基本上有一长串请求处理代码。当请求到达时,控制器调用模型中的业务逻辑。控制器还处理其他与视图相关的事情,例如安排视图(制表符)或启动用户输入的对话框(覆盖文件?)以及模型不应该知道但影响的其他内容(抛出新的NoOverWritePermissionException())

完成模型更改后,控制器会决定是否必须发送更新通知。 (通常是这样)。这样,模型类不需要监听或发送消息,只需要处理业务逻辑和一致状态。更新通知由片段广播和接收,然后运行" updateFromModel()"。

功效:
命令是全局的。可以从可以访问DDS的任何地方发送任何ViewRequest或其他类型的请求。片段不必提供侦听器类,并且没有更高的实例必须为其实例化片段实现侦听器。如果新片段不需要新的请求,则可以在不对控制器类进行任何更改的情况下添加它
模型类根本不需要了解通信。保持一致状态并处理所有数据管理可能很困难。不需要消息处理或会话状态处理。然而,该模型可能不会受到来自观点的恶意电话的保护。但这是一个普遍的问题,如果模型必须在某些时候给出引用,则无法真正防止这种情况。如果你的应用程序适用于只能传递副本/平面数据的模型。但在某些时候,ArrayAdapter只需要访问他应该在gridview中绘制的位图。如果您买不起副本,那么您总是有可能会对模型进行更改调用"。不同的战场......

更新通话可能过于简单。如果片段的更新很昂贵(OpenGL片段重新加载纹理......),您希望获得更详细的更新信息。控制器可以发送更详细的通知,但它实际上不应该/能够知道模型的哪些部分确切地改变了。从模型发送更新说明很难看。模型不仅必须实现消息传递,而且混合通知也会变得非常混乱。控制器可以使用主题将更新通知和其他更新通知分开。例如。更改视频资源的特定主题。这样片段可以决定他们订阅哪些主题。除此之外,您希望拥有一个可以查询更改值的模型。时间戳等我有一个应用程序,用户在画布上绘制形状。它们被渲染为位图并在OpenGL视图中用作纹理。我当然不想每次都重新加载纹理" updateFromModel()"在GLViewFragment中调用
依赖规则:
可能不会一直受到尊重。如果控制器处理一个制表符开关,它可以简单地调用" seletTab()"在TabHost上,因此对外圈有依赖性。您可以将其转换为消息,但它仍然是逻辑依赖项。如果控制器部件必须组织视图的某些元素(通过image-gallery-fragmen-tab加载图像后自动显示image-editor-fragment-tab),则无法完全避免依赖。也许你可以通过建模视图状态来完成它,并让你的视图部分从viewstate.currentUseCase或类似的smth组织起来。但是,如果您需要对应用程序视图进行全局控制,那么我将会遇到此依赖关系规则的问题。如果您尝试保存某些数据并且模型要求覆盖权限,该怎么办?您需要为此创建某种UI。再次依赖。您可以向视图发送消息,并希望DialogFragment选择它。如果它存在于您链接中描述的极其模块化的世界中
实体:
 是我的方法中的模型类。这与您提供的链接非常接近
用例:
我现在没有明确建模的那些。 Atm我正在为视频游戏资产编辑工作。在一个片段中绘制形状,在另一个片段中应用着色值,在galleryfragment中保存/加载,在另一个片段中导出到纹理图集......这样的东西。我会将Use Cases添加为某种Request子集。基本上一个用例作为一组规则,请求允许/要求/期望/禁止等等。我会像交易一样构建它们,以便用例可以继续进行,可以完成,可以取消甚至滚动背部。例如。用例将定义保存新绘制图像的顺序。包括发布一个Dialog以请求覆盖权限,如果没有给予权限或者超时则回滚。但是,用例以许多不同的方式定义。有些应用程序只有一个用例可用于一小时的活动用户交互,有些应用程序只有50个用例只是为了从atm获取资金。 ;)

接口适配器:
这有点复杂。对我来说,这似乎是Android应用程序的极高水平。它声明"接口适配器环包含GUI的整个MVC架构"。我无法真正理解这一点。也许你正在构建比我更复杂的应用程序。

框架和驱动程序:
不知道怎么想这个。 "网络是一个细节,数据库是一个细节..."并且图形包含" UI"在这个戒指中也是如此。我的小脑袋太多了
让我们检查另一个"断言"
独立于框架。该体系结构不依赖于某些特征库软件库的存在。这允许您使用此类框架作为工具,而不必将您的系统塞入其有限的约束中。
嗯,好吧,如果你运行自己的建筑,那就是你得到的
测试。可以在没有UI,数据库,Web服务器或任何其他外部元素的情况下测试业务规则。
在我的方法模型中,类既不知道控制器或视图也不知道消息传递。人们可以仅用那些类来测试状态的一致性
独立于UI。 UI可以轻松更改,而无需更改系统的其余部分。例如,可以使用控制台UI替换Web UI,而无需更改业务规则。
对于Android来说再次有点矫枉过正了吗?独立是的。在我的方法中,您可以添加或删除片段,只要它们不需要在更高的位置进行显式处理。但是用控制台UI取代Web UI并让系统像以前一样运行,这是建筑狂热的梦想。一些UI元素是所提供服务的组成部分。当然,我可以轻松地将画布绘图片段替换为控制台绘图片段,或者使用控制台'拍摄照片​​的经典照片片段。片段,但这并不意味着应用程序仍然有效。从技术上讲,它在我的方法中很好。如果您实现ascii控制台视频播放器,您可以在那里呈现视频,并且该应用程序的其他任何部分都不一定关心。但是,可能是控制器支持的请求集与新控制台UI不一致,或者用例不是为需要通过控制台界面访问视频的顺序而设计的。这种观点并不总是不重要的呈现奴隶,许多建筑大师喜欢将其视为
独立于数据库。您可以换掉Oracle或SQL Server,用于Mongo,BigTable,CouchDB或其他东西。您的业​​务规则未绑定到数据库。
所以啊?这与您的架构有何直接关系?使用正确的适配器和抽象,你可以在hello world应用程序中使用它
独立于任何外部机构。事实上,您的业务规则根本不了解外部世界。
同样在这里。如果你想要模块化的独立代码,那就写吧。很难说具体的。