12 KiB
开发规约
一、总体架构与设计原则
(一)MVVM 架构概述
在软件开发中,为实现良好代码结构、可维护性与可扩展性,应分离视图逻辑与业务逻辑,MVVM 设计模式是有效的解决方案。 View(视图层):包含用户界面控件与 XAML 元素,是用户与应用交互界面,负责展示数据和接收用户操作。应保持简洁,避免复杂业务逻辑,依据 ViewModel 数据展示,并将用户操作传递给 ViewModel 处理。 ViewModel(视图模型层):是视图层和模型层的桥梁,处理控件数据绑定,将视图层控件属性与自身属性绑定实现数据双向流动,还处理视图交互逻辑,调用模型层方法完成业务功能并反馈结果给视图层。 Model(模型层):处理数据和业务逻辑,包括数据获取、存储、处理和验证。对于复杂数据关系,可借助 ORM 技术或自定义数据结构;简单数据存储用普通类和属性。模型层独立于视图层和视图模型层,保证可复用性。 通过将控件属性绑定到 ViewModel,再由 ViewModel 与 Model 交互,实现数据和逻辑在各层有效流转,提高应用可维护性和扩展性。
二、具体实现细节
(一)ViewModel
1. 命令定义与实现
(1)命名规范
ViewModel 中的命令应采用 “动词 + 名词 + Command” 格式命名,清晰表明功能。对应的执行方法命名与命令中的动词一致,便于理解命令与执行逻辑的关联。
(2)代码位置
与视图交互的命令在对应的 ViewModel 类中定义和实现,ViewModel 类放在 “ViewModels” 文件夹下。大型项目中,按功能模块细分该文件夹,如 3D 模型相关的 ViewModel 放在 “ViewModels/3DModelViewModels” 文件夹。
(3)实现方式
使用合适的命令类实现:建议用实现 ICommand 接口的命令类,如自定义的 “RelayCommand”。它要处理执行逻辑和可执行状态判断,依据条件(如数据准备情况、用户权限)决定命令是否可执行。 命令初始化与方法绑定:在 ViewModel 构造函数中初始化命令并与执行方法绑定,如 “LoadModelCommand = new RelayCommand (LoadModel);”,确保命令触发时调用正确执行方法。 参数处理:命令执行需传递参数时,命令类要能正确处理参数传递和接收。修改命令类构造函数或执行方法以接受参数,在视图层绑定命令时设置参数值。
2. 数据绑定属性
(1)命名规范
数据绑定属性名称用驼峰命名法,清晰反映绑定的数据内容,如 “DisplayName” 绑定文本框显示文本,“DataList” 绑定列表数据。
(2)数据类型选择
根据绑定控件需求和数据性质选择合适数据类型。简单文本显示控件用 “string” 类型;列表控件用 “ObservableCollection” 等集合类型,实现数据更新自动刷新。
(3)通知机制
数据绑定属性值变化时,用合适机制通知视图更新。在支持数据绑定的框架(如 WPF)中,可利用相关接口(如 WPF 的 “INotifyPropertyChanged” 接口)。示例如下:
private string _displayName;
public string DisplayName
{
get { return _displayName; }
set
{
_displayName = value;
OnPropertyChanged(nameof(DisplayName));
}
}
属性 “set” 方法调用时,通过 “OnPropertyChanged” 方法通知视图层属性值变化,促使控件更新显示内容。
3. 事件处理与通知
(1)事件命名
ViewModel 中通知视图的事件命名采用 “名词 + 过去分词” 形式,表明动作完成或状态改变,如 “ModelLoaded” 表示模型加载完成,便于理解事件含义和业务逻辑状态。
(2)事件参数类型
事件参数类型准确反映事件携带的信息。如 “ModelLoaded” 事件传递加载后的模型对象,参数类型为 “Model3D”。若事件需传递多个信息,创建自定义事件参数类封装信息。
(3)事件触发
业务逻辑完成后(如模型加载成功、数据保存完成),在合适位置触发事件,用标准事件触发机制,如 “ModelLoaded?.Invoke (model);”。触发前确保事件初始化和订阅正确,避免空引用等问题。
(二)View
1. XAML 结构与布局
(1)命名空间引用
XAML 文件开头只引入实际使用的命名空间,按字母顺序排序,减少复杂性、提高编译速度,如: .xaml
<Window x:Class="YourNamespace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:HelixToolkit="clr - namespace:HelixToolkit.Wpf;assembly=HelixToolkit.Wpf"
Title="3D Model Viewer" Height="450" Width="800">
(2)布局设计原则
选择合适的布局容器:优先用合适布局容器实现灵活可维护布局。简单水平或垂直排列控件用 “StackPanel”,通过 “Orientation” 属性控制排列方向。需精确行列布局用 “Grid”,通过定义行和列定位控件。 避免绝对坐标定位:尽量不用绝对坐标定位控件,以免界面在不同分辨率和窗口大小下适应性差。使用布局容器可实现自适应,保证良好显示效果。
(3)控件命名
为重要控件命名,采用驼峰命名法且以控件类型开头,如 “btnLoadModel”(加载模型按钮)、“txtInputFileName”(输入文件名文本框),提高代码可读性。复杂界面可分组控件并命名,方便在代码 - behind 中访问和操作,如 “LoginControls” 包含登录相关控件。
2. 数据绑定与命令绑定
(1)绑定表达式规范
数据绑定表达式:数据绑定用 “{Binding Path=PropertyName}” 格式,“Path” 可省略(绑定当前数据上下文直接属性时)。绑定深层属性需指定 “Path” 值,如: .xaml
<TextBlock Text="{Binding DisplayName}" />
<TextBlock Text="{Binding UserInfo.Age}" />
命令绑定表达式:命令绑定用 “{Binding CommandName}” 格式,确保命令在 ViewModel 数据上下文中存在,如: .xaml
<Button Content="Load Model" Command="{Binding LoadModelCommand}" />
(2)绑定源设置
在视图根元素(“Window”、“UserControl” 等)设置数据上下文,用 “DataContext” 属性绑定到 ViewModel 实例。可在 XAML 或代码 - behind 中设置。
XAML 中实例化 ViewModel:如: .xaml
<Window.DataContext>
<local:MyViewModel />
</Window.DataContext>
代码 - behind 中设置数据上下文:如:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
}
3. 代码 - behind 使用规范
(1)减少逻辑复杂度
代码 - behind 文件(如 “MainWindow.xaml.cs”)主要处理视图特定初始化和事件订阅等与视图相关操作,避免放复杂业务逻辑或数据处理代码。将业务逻辑和数据处理放 ViewModel 层和模型层,保持视图层简洁,降低耦合度,便于维护和扩展。
(2)事件处理方法命名
代码 - behind 中处理视图事件的方法命名遵循 “控件名称 + 事件名称” 格式,如 “btnLoadModel_Click”,清晰反映事件来源和处理逻辑,提高代码可读性。
(3)资源释放与清理
视图关闭或不再使用时,在代码 - behind 中释放相关资源,如关闭文件流、取消定时器。妥善处理可能导致内存泄漏的对象(如事件订阅),可在合适事件处理方法(如 “Window_Closing”)中添加资源释放代码。
三、代码组织与项目结构
(一)项目文件夹结构
1. 根目录
项目文件:包含解决方案文件(.sln)管理项目文件和关系,项目配置文件(.csproj 等)描述项目设置(如引用、编译选项、目标框架)。 README 文件:简要介绍项目,包括功能、构建和运行步骤、主要依赖项(第三方库、框架版本及来源),方便其他开发人员了解项目。 LICENSE 文件:若项目开源或有特定授权许可,放相应许可证文件,明确使用权限和限制。
2. ViewModels 文件夹
存放所有 ViewModel 类,按功能模块细分子文件夹。每个子文件夹的 ViewModel 类功能围绕所属模块,提高内聚性和可维护性。
3. Views 文件夹
存放所有 XAML 视图文件,按功能模块或界面部分划分子文件夹,如 “MainViews” 放主窗口视图文件,“SettingsViews” 放设置界面视图文件,便于修改相关界面时定位视图文件。
4. Models 文件夹
存放所有模型类,按模型类型细分文件夹。如 “DatabaseModels” 放与数据库相关模型类(用数据库技术处理数据存储和检索),“InputModels” 放处理用户输入数据的模型类(封装、验证和处理用户输入),提高代码可读性和可维护性。
5. Services 文件夹
存放与外部服务交互的类,为模型层或视图模型层提供服务。如网络服务调用类处理网络通信,文件系统操作服务类处理文件操作,使外部交互逻辑清晰,便于维护和扩展。
6. Helpers 文件夹
放复用的辅助类和工具函数,如数据转换类、字符串处理工具类、通用数学计算类。避免代码重复,提高复用性和可维护性。
7. Resources 文件夹
存放项目资源文件,按类型细分文件夹。如 “Images” 放图片资源(用于界面装饰、图标显示),“Styles” 放样式文件(定义界面风格),若支持多语言,“Localization” 放本地化资源文件(实现国际化和本地化),方便管理和使用资源。
8. Language 文件夹
存放语言国际化资源文件,可分为en_US.resx、zh_CN.resx 等,用于实现国际化和本地化。
(二)代码文件结构
1. 每个类的结构
- 命名空间声明:代码文件开头声明类所属命名空间,反映类在项目中的位置和功能,如 “YourNamespace.ViewModels”、“YourNamespace.Models.DatabaseModels”,避免类名冲突,使代码结构清晰。
- 类的组织:类成员按功能和访问权限分组。先公共属性(按功能相关性排列),再公共方法(按功能逻辑排列),接着私有属性和私有方法(也按功能分组)。事件可放在方法后或根据与特定方法关联程度安排,便于理解成员关系和逻辑。
- 注释与文档:为类、方法、属性和重要代码块添加注释,解释功能、目的、参数和返回值(若有)。公共成员用 XML 文档注释格式,方便生成文档,
- 如:
/// <summary>
/// This class represents the ViewModel for the main window.
/// It handles the data binding and command logic related to the main window view.
/// </summary>
public class MyViewModel
{
//...
}
方法注释详细描述功能、输入参数含义和要求、返回值类型和意义,如:
/// <summary>
/// Loads the user data from the database.
/// </summary>
/// <param name="userID">The unique identifier of the user.</param>
/// <returns>An instance of UserData if the user exists, otherwise null.</returns>
public UserData LoadUserData(int userID)
{
//...
}
2. 代码依赖管理
尽量减少循环依赖:避免类之间的循环依赖(类 A 依赖类 B,类 B 又依赖类 A),这种情况会增加逻辑复杂性,导致编译和运行问题。出现循环依赖时,重新设计类结构或提取公共接口打破循环,如创建新抽象层或接口提取共同依赖部分。 依赖注入(如果适用):使用依赖注入框架时,遵循最佳实践管理对象依赖关系。在合适位置(如 ViewModel 构造函数)注入依赖对象,提高可测试性和可维护性。 例如:
private IDataService _dataService;
public MyViewModel(IDataService dataService)
{
_dataService = dataService;
//...
}
在测试 ViewModel 时可模拟数据服务类进行单元测试,修改数据服务类实现时不影响 ViewModel 代码(保持接口一致)。使用依赖注入框架时,正确配置和管理依赖关系(注册依赖项、解析依赖对象等)。