【问题标题】:Complex software architecture复杂的软件架构
【发布时间】:2009-10-01 20:16:26
【问题描述】:

我有几个关于我正在开发的软件架构的问题!

所以基本上,这个软件允许用户访问一些流行的网站:

  • 社交网络(Facebook、MySpace、...)、
  • 公共服务(RSS、邮件、Twitter...),
  • 社交书签(Digg、Delicious...),
  • 聊天(MSN、AOL...),
  • ...

目前架构如下: 使用 MVC 和 Observer/Observable 设计模式在模型(TApp_Core)和用户界面之间进行交互。

TApp_Core
 TBookmarkingServices_Core
  TDelicious (implement IBookmarkingServices) 
  TDigg (implement IBookmarkingServices) 
  etc... (implement IBookmarkingServices) 
 TChatServices_Core 
  TMSN (implement IChatServices) 
  TGoogleChat (implement IChatServices) 
  TAOLChat (implement IChatServices) 
  etc... 
 TRSSServices_Core 
 ...

因此软件创建了一个 TApp_Core 实例,然后根据用户的选择,它创建了一些其他服务的实例(例如:App_Core.BookmarkingServices_Core.AddServices(Digg, User, Password);)。

一些问题!

  • 您认为目前的软件设计对吗?
  • 目前所有软件只有一个线程...为每个服务请求创建一个新线程会更好吗? (例如,TDigg 从一个按钮接收消息,它创建一个线程,该线程将创建一个 TidHTTP,向服务器生成请求,等待响应,解析响应,向每个观察者发送消息(回调)然后释放线程。
  • 似乎很难将所有视图/控制器与模型的每个部分链接起来,这是否正常需要这么多工作? 例如:要发送一条消息,例如使用 Twitter,它需要:
    • 将控制器(按钮)附加到 TApp_Core.TMicrobloggingServices_Core.TTwiter 对象(模型)
    • 等待用户点击按钮
    • 向 TTwiter(模型)发送消息
    • 创建一个线程将请求发送到服务器
    • 解析来自服务器的响应
    • 执行回调以通知请求已执行并给出结果
    • 释放线程
  • 如果我用之前的思路,会不会造成线程过多,导致电脑变慢,不负责任?除非我实现了一个线程池(但使用起来相当复杂)。
  • SQLite 3 是否足以在客户端存储所有数据? (数据可以是邮件、RSS 提要等……所以随着时间的推移,它可能是相当多的数据)。

感谢和抱歉我的英语不好!

【问题讨论】:

    标签: delphi model-view-controller design-patterns social-networking


    【解决方案1】:

    你觉得现在的软件设计对吗?

    可能没有客观上“正确”的设计——只有更好或更差的设计。 :) 看起来您走在正确的轨道上,但我建议您将 GUI 和视图与后端服务分离。例如:

         View --- Controller --- Service
    

    所以View 只知道如何呈现事物(即 IM 消息、Web 内容或其他)并将来自用户的请求传递给ControllerController 接收来自Service 的通知,并更新ViewService 对前端一无所知(因此它可以是可编写脚本的,这非常有用)并且只实现与网络服务通信所需的协议。对于每个不同的后端系统,您将拥有一组单独的这三个类,以及一个用于在全局级别管理应用程序的整体控制器。

    似乎很难将所有视图/控制器与模型的每个部分联系起来,这是否正常,需要这么多工作?

    可能,是的 - 您正在尝试一个重要的应用程序。我假设您使用的是 MVC 风格的架构。可能需要更多的前期工作才能正确解耦,但这绝对是值得的。您的应用程序越复杂,您就越能从分层和解耦中受益。

    为服务的每个请求创建一个新线程会更好吗?

    鉴于所有服务都是基于网络的,您需要防止阻塞 I/O 调用(例如套接字读取)阻塞您的主 GUI 线程。因此,您要么需要在一个线程上使用带有回调的完全异步 I/O(不是很常见),要么为每个网络会话设置一个单独的线程(最常见的方法)。我不建议为每个单独的请求生成一个新线程,而是为整个会话生成一个新线程。您需要小心避免常见的线程问题,例如竞争条件和死锁(这个主题可能会写满一本书)。该线程需要将消息发回 GUI 以获取通知,因为您无法从另一个线程的上下文中更新您的 GUI。

    (例如,TDigg 从按钮接收消息,它创建一个线程,该线程将创建一个 TidHTTP,向服务器生成请求,等待响应,解析响应,向每个观察者发送消息(回调)然后释放线程。

    听起来您的 GUI 与控制器和协议处理程序耦合在一起。我建议你将前端和后端分开,并保持解耦。

    如果我用之前的思路,会不会造成线程过多,导致电脑变慢,不负责任?

    线程通常用于使计算机更多响应。创建线程有一定的开销,并且需要非常小心才能正确同步。但是,如果每个网络会话有一个线程,开销不会减慢速度,因为您最多只有几个线程。

    SQLite 3 是否足以在客户端存储所有数据? (数据可以是邮件、RSS 提要等......所以随着时间的推移它可能是相当多的数据)。

    SQLite 是用于此目的的优秀数据库。它被世界上一些最大的软件公司用于客户端存储管理。它非常快速和高效,我相信它能够跟上您的应用程序。

    您似乎正在执行一项复杂的应用程序。我建议你买一本关于并发编程的书,并阅读更多关于线程的内容。这是一个非常复杂的问题,如果不仔细编程,可能会导致难以追踪的错误。祝你好运!

    【讨论】:

    • 非常感谢您的帮助 :) - 我会尝试将控制器与视图/模型分离。 - 如果每个服务仅由一个线程表示,如果用户尝试请求 2 个或更多请求,会不会有问题?我的意思是线程将只能处理 1 个请求,并且可能还没有完成从服务器取回它需要再次处理另一个请求的响应。 - 我正在尝试阅读有关线程的书籍和文章,但正如您所说,这很复杂;)
    • 这取决于服务和协议的性质。采用http 样式的请求/响应模型,您通常只会有一个未完成的请求。我期望的大多数服务都只有一个会话(例如 IM),这意味着您可以将线程的范围与会话的范围相匹配。如果你想支持多个请求,你可以简单地使用一个请求队列并按顺序处理它们。
    【解决方案2】:

    我建议使用 ORM 框架从您的数据库中抽象出来。这为您提供了一个有意义的数据抽象层,也意味着您不必担心您选择的数据库。 SQLite 是一个不错的紧凑型数据库。测试可能没问题。但它肯定不会在真正的多用户环境中削减它(它具有文件级锁定,这肯定会使任何有意义的规模上的并发 CRUD 成为不可能)。它也只使用 SQL 的一个子集。使用和 ORM,您可以在以后轻松更改。

    【讨论】:

    • 感谢您的回答!我可以使用哪个嵌入式数据库来替换 SQLite? SQLite 不用于服务器,仅用于在客户端计算机上存储客户端数据。并发访问应该没有任何问题,因为我使用锁来读/写数据库并且每个数据库只使用一个连接。
    • 一个完整的 ORM 对于这个应用程序来说可能是多余的。无论如何,数据持久性都可以在后端隔离,并隐藏在实用方法后面(例如saveFeed() 等)。鉴于该应用程序是单用户客户端应用程序,因此不需要完整的多用户支持 - 正如我阅读问题一样,它不需要扩展。
    【解决方案3】:

    查看您的帖子很难确定您的架构是否“好”。这是我努力的目标

    • 有效吗?
    • 尽量做到这一点。
    • 随时清理您的设计。
    • 使用工具可视化您的设计。

    我发现使用像 NDepend 这样的工具来可视化依赖关系非常有帮助。

    【讨论】:

    • 目前,模型和视图/控制器的链接不是很紧密,因为将这两个部分链接在一起需要做很多工作(大部分是实现服务的代码的一半)。恐怕如果我没有正确的架构,以后更改它会变得一团糟。我查看了 NDepend,但它似乎更适用于 .NET 和 Visual Studio(我既不使用 .NET 也不使用 VS)。