WPF的MVVM中的Model与ViewModel的结构思路总感觉不对

jiuzaizuotian2014 2017-12-29 11:25:54
请教高手关于Model与ViewModel的设计问题。
Model中存在各种关联关系,比如:Company中包含List<Department>,而department中包含List<Team>,Team中包含List<Employee>,这样就会形成层次结构。这样为了不把model直接暴露给view,要通过viewmodel来隔离,这样在viewmodel中就把model中的属性包装一下再暴露给view。这样,viewmodel中也会形成与model中相同或相似的层次结构,感觉起来把问题弄复杂了,感觉哪里不对似的。例如如下代码:
class Company
{
List<Department> Departments{get;set;}
public string CompanyName{get;set;}
....//other properties and methods
}
class Department
{
List<Team> Teams{get;set;}
public string DepartmentName{get;set;}
....//other properties and methods
}
class Team
{
List<Employee> Employes{get;set;}
public string TeamName{get;set;}
....//other properties and methods
}
class Employee
{
public string EmployeeName{get;set;}
}
那么在设计ViewModel的时候,由于view显示的需要,是不是都要弄一个对应的viewmodel类呢?比如:
class CompanyViewModel:INotifyPropertyChanged
{
public CompanyViewModel(Company company)
{
_company=company;
}
private Company _company;
private ObservableCollection<DepartmentViewModel> _department;
public ObservableCollection<DepartmentViewModel> Departments
{
get
{
if(_department==null)
_department=new ObservableCollection<DepartmentViewModel>();
foreach(var d in _company.Departments)
_department.Add(new DepartmentViewModel(d));
return _department;
}
}

public string CompanyName
{
get
{return _company.CompanyName;}
set
{
_company.CompanyName=value;
OnPropertyChanged("CompanyName");
}
}
....//其它属性、方法、字段、event等省略
}


class DepartmentViewModel:INotifyPropertyChanged
{
public DepartmentViewModel(Department department)
{
_department=department;
}
private Department _department;

private ObservableCollection<TeamViewModel> _teams;;
public ObservableCollection<TeamViewModel> Teams
{
get
{
if(_teams==null)
_teams=new ObservableCollection<TeamViewModel>();
foreach(var t in _department.Teams)
_teams.Add(new TeamViewModel(t));
return _teams;
}
}
public string DepartmentName
{
get
{return _department.DepartmentName;}
set
{
_department.DepartmentName=value;
OnPropertyChanged("DepartmentName");
}
}


....//其它属性、方法、字段、event等省略
}

class TeamViewModel:INotifyPropertyChanged
{
public TeamViewModel(Team team)
{
_team=team;
}
private Team _team;

private ObservableCollection<EmployeeModel> _employees;;
public ObservableCollection<EmployeeModel> Employees
{
get
{
if(_employees==null)
_employees=new ObservableCollection<EmployeeModel>();
foreach(var e in _team.Employees)
_employees.Add(new EmployeeModel(e));
return _employees;
}
}
public string TeamName
{
get
{return _team.TeamName;}
set
{
_team.TeamName=value;
OnPropertyChanged("TeamName");
}
}

....//其它属性、方法、字段、event等省略
}
class EmployeeViewModel:INotifyPropertyChanged
{
public EmployeeViewModel(Employee employee)
{
_employee=employee;
}
private Employee _employee;

public string EmployeeName
{
get
{return _employee.EmployeeName;}
set
{
_emplpyee.EmployeeName=value;
OnPropertyChanged("EmployeeName");
}
}

....//其它属性、方法、字段、event等省略
}

在以上的代码中,Model层和ViewModel层都形成了相同的层次结构,结构更加复杂了,但是问题还不仅如此:
如果所有的移动对象(比如在EmployeeA发生调动,需要从TeamA调动到TeamB,这样一个业务逻辑,如果放在ViewModel层去写,则可以放置在DepartmentViewModel中去完成逻辑,即把teamA中的这个雇员移动到teamB中,移动结束后ViewModel层是知道改变的,ObservableCollection会自动通知View层改变)。如果放在Model层去写EmployeeA的调动逻辑,然后通过ViewModel来调用这样的逻辑,则ViewModel层不知道Emplpyee,view是不会发生改变的,能想到的方式是把Model都实现INotifyProperty接口,并给属性添加上OnPropertyChanged方法调用,同时将Model对象中的集合属性由List改为ObservableCollection<>,并在对应的ViewModel中订阅PropertyChanged事件和CollectionChanged事件,当model的属性发生改变时,对应的ViewModel中通过方法来同步进行改变。
总结下:(1)如果在ViewModel中写业务逻辑,Model的所有属性改变必须由ViewModel来完成,那么感觉viewmodel对象中的逻辑太多,又不像是MVVM所说的VM的功能,程序对象多了以后会把ViewModel弄得特别复杂,就像是PresentationLogic和BusinessLogic都由VM承担了似的,Model层只是写了一些Entity似的。
(2)如果业务逻辑放在Model中写,则在一个Model对象中改变了属性的话,就需要为Model实现INotifyPropertyChanged接口和使用ObservableCollection集合类,然后由ViewModel订阅Model的PropertyChanged和CollectionChanged事件实现同步改变并通知View发生响应。这样感觉自来就弄得特别复杂,必须小心翼翼地考虑没一个Model属性改变如果通过事件来通知ViewModel发生响应,并且性能也会受到很多影响。

问题:总感觉这两种途径都有问题,不知大家都是怎么考虑的呢?还请前辈分享心得,不胜感激,谢谢[/b]
...全文
1930 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
jiuzaizuotian2014 2018-01-18
  • 打赏
  • 举报
回复
谢谢给为指点,学习了
大然然 2018-01-02
  • 打赏
  • 举报
回复
引用 9 楼 xuzuning 的回复:
或者你还可以这样理解 比如 Model 从数据库了查询得 sex 字段为 1,你需要将其显式成 男 或 male,当然你是可以在查询时就对照好的,但这显然需要传递过去辅助信息,以确定是 1 还是 男 还是 male 另一种选择就是将转换写到 View 中,这就需要 View 中是含有显示逻辑的了 如果把 通用的显示逻辑提取出来,那就是 ViewModel(视图模型) 如果不是这样的话,MVVM 就是 MV(Model View) 如果说 Model 是整体的业务逻辑的话,那么 ViewModel 就是 View 的业务逻辑
sex的例子,一把都是写个convert , view bind的时候加上
圣殿骑士18 2017-12-31
  • 打赏
  • 举报
回复
很赞同p哥的观点。 我的系统实现也是如此,ViewModel是要关注的核心点,我在后端使用的是EF6,它也有model,这个model是因为要配合EF的实现才建立的,其实这个model对于业务层面来说并不重要,只是实现了代码书写层面的方便。 对于你的问题,说直白一点,就是你只需要关注ViewModel,忘记你的model,这样当然就没有了你纠结的如何在ViewModel和Model之间倒数据的问题。而在后端代码实现上,也不需要纠结model,而应该关注你具体的业务,比如如何以简约的方式实现持久化。
jiuzaizuotian2014 2017-12-30
  • 打赏
  • 举报
回复
这不是实际项目的model,就是说model中很多个entity类之间存在相互之间的关键、聚合、组合等关系,考虑在建筑模型中使用(建筑涉及结构、构件、点 线、面、坐标系、缺陷、缺陷图纸等以及其他类对象,这些对象之间具有相互依赖关系),当然个人觉得各种管理系统也是这种情况(随便找一个数据库模型图也都是有各种依赖关系),如何去设计model和viewmodel呢?参看网上一些资料,有人讲讲在model中实现INotofyPropertyChanged,把model直接暴露给view,也有人将不能将model暴露给viewmodel,会把view层和model也耦合到一起去造成复杂性。实际上我们也希望model层的类(业务逻辑、数据持久化、entities)能够较为独立,并希望其能够在wpf桌面程序和web程序中重复利用而已。感觉MVC思路就简单一些,直接用model来渲染view(MVC中数据归数据,而不是ViewModel这种既要表现成数据的样子来渲染view而又不是真正的数据),而MVVM说把View和Model解耦了,感觉是不是MVVM弄得更复杂了啊,还是说我的思路不对,所以请教高手们看看怎么做这种结构,谢谢
  • 打赏
  • 举报
回复
数据处理是在任何一个程序中都会涉及到的,所以容易被反反复复拿出来争论。但是你要看看自己是从什么角度来分析的,这样自己就能发现问题。如果你发现自己是从底层数据库表设计出发,从底向上拼凑个层 Model 使用需求,你就能理解自己为什么总也不能轻松地谈论前边万化的用户交互界面需求设计,就会理解自己为什么越来越怕数据设计复杂性。如果你是从前端需求出来设计前端 ViewModel,那么你一开始就根本不关心什么 Model,你会把精力放在轻松地设计好那些底层可能根本没有的数据上(比如 SearchYear、SearchMongth 的应用设计),你就不觉得“越来越复杂、越来越不对头”,而反而会随时发现自己还有程序不够灵敏、不够丰富、用户用起来不够酷。
  • 打赏
  • 举报
回复
当你使用 WPF 作为前端开发工具来设计开发一个服装企业(比如说有20个加工厂,有40名业务人员、有10位专门在外边量体的师傅),设计这样一个企业应用网络系统的时候,首先地,你需要按照三层架构的概念来在第一阶段的基本功能中在远程 server 服务系统上开放20个服务功能,然后同时开发你的 WPF 客户端界面程序。你不能说自己“开放50个数据库表”然后同时开发WPF客户端界面程序吧?那就不叫真正的c/s架构网络软件设计。业务逻辑实际上在服务器端系统来控制,这个服务器的BLL层控制了整个企业核心数据接口和功能,对前端隐藏了后台核心技术。 而 WPF 的前端应用自然也客户说“业务逻辑”这个词儿,但是这时候就专门指的是用户交互界面需求,各种复杂表单,如何让用户体验超级棒,这才叫做业务逻辑。 前后端的业务逻辑概念的重点是不一样的。前端的设计从用户交互界面设计文档出发,必须考虑到这个方面。所以满脑子只有数据库表、兴趣只在这方面语法上的人很难做好前端设计。
  • 打赏
  • 举报
回复
从你的描述还可以看出,你通常把 WPF 或者 asp.net 当作一个单机的程序来设计,而不是真正的 c/s 架构网络系统来设计。 一个真正的 c/s 架构网络系统,主要的业务逻辑层都在专门开发的企业 server 端来实现,而 WPF 或者 asp.net 应用专注于表现层的客户端程序,你怎么可能动不动就说“业务逻辑层放在Model中写”这类话呢?只有大多数时间只写单机程序(包括asp.net mvc 程序中直接增删改查数据库表而根本不知道需要另外设计独立的企业服务系统)才容易这样把原本是专门针对前端的 MVC、MVVM 跟后台处理系统混淆起来。 MVC 是最古老、也是最简单的前端框架。MVP 比 MVC 实用100倍。而 MVVM 要比 MVP 简洁、精炼、清晰得许多倍(当然MVVP框架设计得不好的话,容易因为太多余自动化,反而造成性能灾难)。但是这些都是前端表现层的技术。如果你首先没有分为三层架构,而是在表现层里边直接纠结什么 Entity、数据库增删改查操作,就不会把精力全都放在研究 MVVM 或者 MVC 技术上。 MVC 非常简单,它是 one-way 的,也就是一遍遍刷新和重建界面的,由此它简化除了 Controoler 概念。而 MVVM 是 two-way 的,强调双向绑定,由此它取消了 Controller 这个含混的东西。这是主要区别。
足球中国 2017-12-30
  • 打赏
  • 举报
回复
数据绑定,这个东西不能用。
xuzuning 2017-12-30
  • 打赏
  • 举报
回复
或者你还可以这样理解 比如 Model 从数据库了查询得 sex 字段为 1,你需要将其显式成 男 或 male,当然你是可以在查询时就对照好的,但这显然需要传递过去辅助信息,以确定是 1 还是 男 还是 male 另一种选择就是将转换写到 View 中,这就需要 View 中是含有显示逻辑的了 如果把 通用的显示逻辑提取出来,那就是 ViewModel(视图模型) 如果不是这样的话,MVVM 就是 MV(Model View) 如果说 Model 是整体的业务逻辑的话,那么 ViewModel 就是 View 的业务逻辑
  • 打赏
  • 举报
回复
随着开发复杂的前端大程序,我们强调 V 和 VM 的设计,尽量避免纠结什么“Entity、三层”之类的古老的概念,而是要求前端开发人员应该去熟练掌握 XAML 组件、html 元素、bootstrap 组件等等 V 的开发方法,以及从需求文档出发立刻写出 VM 代码的能力。 可以说,前端开发人员最好是根本不写任何一行数据库相关代码,他只是调用几个 BLL 接口而将 Model 实体当作参数进行输入输出而已。(但是他需要调试数据库中的数据,这也就意味着他需要了解 Model 概念)。 满脑子只有数据库表的人,会把比较多时间放在 Model 上 纠结不清。其实数据库表虽然很基础,但是增删改查数据库表相对于从千变万化的业务需求文档出发来配置 View 和重构 VM能有多少技术含量啊?其实并不需要花那么多时间争论数据库增删改查的问题。
  • 打赏
  • 举报
回复
基本上对于前端程序员来说,在 MVVM 模型中,你甚至可以忽略 Model,把几乎全部精力用在分别设计 V 和 VM 上,能直观地看到这两张皮如何轻松地绑定起来,就能精通前端设计。否则如果一切都靠“自底向上地写组件代码”,那么就不可能理解 VM 的作用,肯定还是传统的那种想到那里写到哪里的做法。Model 基本上可以忽略。(当然你在前后台通讯的时候,还是要有 Model 概念,哪怕只是按照 json 或者 xml 格式来传接口数据,这也就是已经使用了 model 了)
  • 打赏
  • 举报
回复
就是你说的 1. 中的情况,ViewModel 本身就是专门要“充血的实体”,而 Model 本恶虎你就是刻意“失血的实体”,ViewModel 是交互界面业务设计,而 Model 仅仅是为了在给前后台传一下简单的数据。 比如说,一个报表界面上有“选择年、选择月”,然后还有一个“查询”按钮,那么ViewModel 中你就会在所有程序员写的 ViewModel 在中看到(这里只是说明属性名称、是伪代码,删去了其它声明和功能代码)
class ViewModel
{
    int SearchYear;
    int SearchMonth;
    Start();

    int Year;
    int Month;
    List<Line> Datas;
}
为什么会有两个 Year 和两个 Month 呢?因为用户可以自由地选择改变“年、月”控件值,然后点按“开始查询”按钮时才会查询数据,那么这里界面上绑定的 Year 肯定就不是当前查询报表的 year,而是 SearchYear。 这就是 ViewModel的基本概念,ViewModel 纯粹是从界面出发的,而不是从什么数据库表出发的。满脑子只有数据库表的人根本不会设计前端界面,所以 ViewModel 必须从前端需求出发、从用户体验出发,这样设计出来许多的 ViewModel 自动化属性、执行方法、事件,都是纯粹为前端界面直接轻松绑定而服务的。 这就是 ViewModel 的作用。一旦设计好 ViewModel,那么前端轻松地绑定 XAML 或者 HTML 就可以快速开发千变万化的界面,少写代码。所以 ViewModel 必定是“充血的”才可能满足这个要求。
exception92 2017-12-30
  • 打赏
  • 举报
回复
Model就是数据载体,千万不要在get/set中写业务逻辑,因为get/set 的作用就是取值赋值。 业务逻辑还是要写在VM中,表结构关系复杂 业务逻辑也就处理复杂了,最好还是VM分开写这样会更新清晰。其实VM也就是个DataContext,如果你在window窗体类代码中实现INotifypropertychanged接口,同样也能通知到View更新,只不过再指定DataContext的时候需要这样:
this.DataContext=this;

但是这就显得后台代码更加混乱,于是就再多定义一个类来作为DataContext。
xuzuning 2017-12-30
  • 打赏
  • 举报
回复
MVC : Model <=> Controller <=>View MVVM : Model <=> ViewModel <=> View MVVM 和 MVC 没有本质上的区别,不过由于 MVC 的松散架构使得 C 往往成了可有可无的了,大多情况下只 MV 就足以应付了,C成了鸡肋 在 MVVM 中,ViewModel 有了具体的职能(数据绑定的通道)不再是可有可无的了 如果你并没有意识到数据绑定的优势,还是沿袭传统的数据指派,那么 MVVM 一样可以退化成 MV 数据指派 是向界面推送数据,并不管界面需要不需要,有备无患嘛 数据绑定 是界面主动去拉数据,需要什么,才生产什么 记得我当年发布我的 拉数据的 php 模本引擎时,得到的是一片反对,不得已又加上了 Assign 曲高和寡喽,其实 MVVM 也一样
masanaka 2017-12-30
  • 打赏
  • 举报
回复
>那么在设计ViewModel的时候,由于view显示的需要,是不是都要弄一个对应的viewmodel类呢? 设计ViewModel的时候,对应的是view,不是model吧? 如果你的view只用于company的基础信息的交互,那viewmodel里就不需要有Departments。 >有人讲讲在model中实现INotofyPropertyChanged,把model直接暴露给view 这个是有问题的,从设计的角度讲。 但是如果只是个结果论者,其实连mvvm模式都可以不用。 >感觉MVC思路就简单一些,直接用model来渲染view 你指的是asp.net mvc吗? 其实思路一样,这里的Model应该是指view model, 如果把data entity直接作为view model来使用,就结果而言是没问题的,但是耦合上,又是一个good design和bad design的问题了。
圣殿骑士18 2017-12-29
  • 打赏
  • 举报
回复
先不说viewmodel,你这样的model设计是实际的项目吗?感觉是学院派的设计,太啰嗦了。实际作用在哪里?model都复杂了,viewmodel当然只会更复杂。

110,535

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧