我当前的类PropertyManager如下所示:
public class PropertyManager : IDisposable
{
private readonly IPropertyRepo _propertyRepo;
private readonly IUserTypeRepo _userTypeRepo;
public PropertyManager(IPropertyRepo propertyRepo, IUserTypeRepo userTypeRepo = null)
{
if (propertyRepo == null)
throw new ArgumentNullException("propertyRepo");
_propertyRepo = propertyRepo;
if (userTypeRepo != null)
_userTypeRepo = userTypeRepo;
}
}我的属性管理器将在某些方法中使用_userTypeRepo来完成某些任务。我想我想要执行这样一条规则:“每个经理(服务、工厂等)都应该对自己的存储库负责。”
这个想法是:
PropertyManager,因为它需要对UserTypeRepo做一些事情,所以我应该使用UserManager来进行这样的活动。
因此,这意味着在创建UserManager实例(即var usrMgr = new UserManager(); // no repo)时,我不会提供回购。相反,UserManager将使用默认构造函数,该构造函数将创建IUserTypeRepo的一个新实例,并提供UserManager的一个新实例,然后它可以完成它的工作。
我认为这实现了一些设计原则,如关注点分离和单一责任分离,但是我可能会摆脱依赖注入设计模式,因为新的管理人员现在将拥有多个构造器,如下所示:
public class PropertyManager : IDisposable
{
private readonly IPropertyRepo _propertyRepo;
public PropertyManager(){
// use the default repo
_propertyRepo = new PropertyRepo();
}
// Used from Controller or Unit Testing
public PropertyManager(IPropertyRepo propertyRepo)
{
if (propertyRepo == null)
throw new ArgumentNullException("propertyRepo");
}
}
public class UserManager : IDisposable
{
private readonly IUserRepo _userRepo;
public UserManager(){
// use the default repo
_userRepo = new UserRepo();
}
// Used from Controller or Unit Testing
public UserManager(IUserRepo userRepo)
{
if (userRepo == null)
throw new ArgumentNullException("userRepo");
}
}会不会对此不屑一顾?还是我在正确的轨道上?在这两种情况下,为什么和感谢?
Update.在阅读了Yawar的帖子后,决定更新我的帖子,我想我有一个相关的问题。
让我们来看看上面的一个真实世界的例子。在现实生活中,我有一个名为"Robert“的PropertyManager,他每天早上在工作中所做的工作之一就是打开Open() Property (也就是说,他打开了他是Manager的Property )。我还有一个UserManger,负责管理访问Property的人,她的名字叫"Sarah“,她有一个叫做EnterProperty()的函数(这就是她早上走进大楼时所做的事情)。
规则: UserManager在使用EnterProperty()时依赖于PropertyManager
按照所有公认的标准,这看起来是这样的:
属性管理器
class PropertyManager : IPropertyManager
{
private readonly IPropertyRepo _propertyRepo;
public PropertyManager(IPropertyRepo propertyRepo)
{
if (propertyRepo == null)
throw new ArgumentNullException("propertyRepo");
this._propertyRepo = propertyRepo;
}
// this is when Robert opens the property in the morning
public void Open()
{
_propertyRepo.Open();
}
// this is when Robert closes the property in the evening
public void Close()
{
_propertyRepo.Close();
}
// this answers the question
public bool IsOpen()
{
return _propertyRepo.IsOpen();
}
}用户管理器
class UserManager : IUserManager
{
private readonly IPropertyRepo _propertyRepo;
private readonly IUserRepo _userRepo;
public UserManager(IUserRepo userRepo, IPropertyRepo propertyRepo = null)
{
if (userRepo == null)
throw new ArgumentNullException("userRepo");
this._userRepo = userRepo;
if (propertyRepo != null)
this._propertyRepo = propertyRepo;
}
// this allows Sarah to physically enter the building
public void EnterProperty()
{
if(_propertyRepo.IsOpen())
{
Console.WriteLine("I'm in the building.");
}else{
_propertyRepo.Open(); // here is my issue (explain below)
Console.WriteLine("Even though I had to execute the Open() operation, I'm in the building. Hmm...");
}
}
}网络API控制器
{
public void OpenForBusiness(){
private const IPropertyRepo propertyRepo = new PropertyRepo();
private IPropertyManager propertyManager = new PropertyManager(propertyRepo);
private IUserManager userManager = new UserManager(new UserRepo(), propertyRepo);
// Robert, the `PropertyManager`, opens the `Property` in the morning
propertyManager.Open();
// Sarah, the `UserManager`, goes into `Property` after it is opened
userManager.EnterProperty();
}
}现在,一切都很酷,我可以离开了,我现在有了一个Repository模式,它使用依赖注入,它支持TDD,而不是紧密耦合的类,还有其他好处。
然而,真正的现实吗?(解释我为什么第二次问)
我认为一种更真实(现实)的方法就是:。
网络API控制器
public void Method1()
{
private IPropertyManager propMgr = new PropertyManager(new PropertyRepo());
private IUserManager userMgr = new UserManager(new UserRepo()); // no dependencies on any repository but my own
// 1. Robert, the `PropertyManager`, opens the `Property`
propMgr.Open();
// 2. Check to see if `Property` is open before entering
// choice a. try to open the door of the `Property`
// choice b. call or text Robert, the `PropertyManager`, and ask him if he opened the `Property` yet, so...
if(propMgr.IsOpen()){
// 3. Sarah, the `UserManager`, arrives at work and enters the `Property`
userMgr.EnterProperty();
}else{
// sol, that sucks, I can't enter the `Property` until the authorized person - Robert - the `PropertyManager` opens it
// right???
}
}EnterProperty()在UserManager上的方法现在看起来是这样的: //允许Sarah物理地进入建筑物的公共空EnterProperty() {Console.WriteLine(“I‘s in the building”);}。
从上面承诺的解释:
如果我们认为在现实世界中,,我们必须同意,后者优先于前者。当想到一个存储库时,让我们说这是一个人的自我(即人)的定义(即,拥有与UserRepo相关的所有数据的User,即与UserManager有关的DNA、心跳、脑波模式等)是人类( HumanRepo)的定义。因此,允许UserManager了解PropertyRepo 和的Open()方法违反了所有真实世界安全原则和业务规则。实际上,这意味着通过我的Contructor(),我可以获得一个PropertyRepo的接口表示,我可以使用我认为合适的任何方式。这是HumanRepo的以下逻辑的同义词
我,萨拉-一个UserManager -通过一个新的实例,通过我的PropertyRepo通过构造器()创建一个PropertyRepo的全息界面,它是我认为合适的任何方式都可以使用的PropertyManager。当然,现在我只想使用PropertyRepo的PropertyRepo方法,如果罗伯特还没有完成他的职责,我实际上使用Open()方法来完成它。,这是我关心的一个安全问题,在现实世界中说,我不需要等待罗伯特打开Property,使用他的全息文件,实现他的Open()方法来访问。
这似乎不对。
我认为,在最后一个实现中,我得到了SoC、SRP、DI、Repository模式、TDD和Logical,并且尽可能接近于现实世界的实现。
你们都怎么想?
发布于 2017-03-15 22:25:02
我认为我同意您的SoC,并将PropertyManager类分解为PropertyManager和UserManager类。你快到了。
我只想重构如下:
public class PropertyManager : IDisposable, IPropertyManager
{
private readonly IPropertyRepo _propertyRepo;
// Used from Controller or Unit Testing
public PropertyManager(IPropertyRepo propertyRepo)
{
if (propertyRepo == null)
throw new ArgumentNullException("propertyRepo");
this._propertyRepo = propertyRepo;
}
}
public class UserManager : IDisposable, IUserManager
{
private readonly IUserRepo _userRepo;
// Used from Controller or Unit Testing
public UserManager(IUserRepo userRepo)
{
if (userRepo == null)
throw new ArgumentNullException("userRepo");
this._userRepo = userRepo;
}
}注意:只提取IPropertyManager & IUserManager,以便调用类依赖于接口并提供实现。
如果要(应该)强制客户端提供IPropertyRepo和IUserRepo接口的具体实现,则创建无参数构造函数是无用的。
public PropertyManager(){
// use the default repo
_propertyRepo = new PropertyRepo();
}我觉得你不需要
if (propertyRepo == null)
throw new ArgumentNullException("propertyRepo");或
if (userRepo == null)
throw new ArgumentNullException("userRepo");由于IPropertyRepo和IUserRepo将在应用程序启动时通过IoC进行解析(比如它的MVC,然后在调用控制器之前IoC会解决它们),所以不需要检查null。我从未在代码中检查过null的依赖项。
从你在这里发布的信息来看,差不多就是这样了。
工作单位模式用于存储库层,而不是管理层。我会把它从标题中删除。
希望这能有所帮助!
发布于 2017-03-15 22:38:57
我认为这实现了一些面向对象的目标,例如分离关注点和单一责任原则。
结果正好相反。现在,PropertyManager与PropertyRepo紧密结合;以前,它们是松散耦合的。
第一种方法比后一种方法更好。但是,PropertyManager和UserManager不应该创建他们赖以完成工作的其他对象。应该将创建和管理对象的责任卸载到IoC容器中。
接口描述可以做什么,而类描述它是如何完成的。只有类涉及实现细节--接口完全不知道如何完成某些任务。因为只有类有构造函数,因此构造函数是实现细节。这的一个有趣的推论是,除了几个例外,您可以考虑新关键字的外观是一个代码嗅觉。- 加里McLean厅
对最新问题的回答:
在您更新的问题中,您将Service/Manager和域组合成一个单独的类- PropertyManager,UserManager。这就变成了个人偏好。
我个人喜欢把它们分开。此外,我喜欢使用基于角色和基于声明的授权。让我用我的GitHub样本项目作为参考。请随意克隆它。
Fluent API也使用User类。
public partial class User
{
public int Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
}public class UserService : IUserService
{
private readonly IRepository<User> _repository;
public UserService(IRepository<User> repository)
{
_repository = repository;
}
public async Task<IPagedList<User>> GetUsersAsync(UserPagedDataRequest request)
{
...
}
}注意,与UI相关的业务逻辑停留在UI层。
public async Task<ActionResult> Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid)
{
bool result = _activeDirectoryService.ValidateCredentials(
model.Domain, model.UserName, model.Password);
if (result)
{
...
}
}
...
}发布于 2017-03-16 04:32:39
您可以采取完全不同的方法.(忽略存储库,但允许注入存储库)
在该系统中,该属性只具有可读性,有一个事件系统来处理突变,事件系统还具有规则系统,该规则系统控制允许哪些突变。这意味着,即使您有一个属性对象,也不能在不遍历其规则的情况下对其进行变异。
这段代码更具有概念性。下一个逻辑步骤是使用一个完整的参与者模型,类似于(akka.net),您可能会发现您的存储库模式正在消失:)
public class Property
{
public string Name { get; private set; }
private IPropertyRules _rules;
private List<User> _occupants = new List<User>();
private IEventLog _eventLog;
public Property(IPropertyRules rules, IEventLog eventLog)
{
_rules = rules;
_eventLog = eventLog;
}
public ActionResult Do(IAction action, User user)
{
_eventLog.Add(action, user);
if (_rules.UserAllowedTo(action, user, this))
{
switch (action)
{
case Open o:
Open();
return new ActionResult(true, $"{user} opened {Name}");
case Enter e:
Enter(user);
return new ActionResult(true, $"{user} entered {Name}");
}
return new ActionResult(false, $"{Name} does not know how to {action} for {user}");
}
return new ActionResult(false, $"{user} is not allowed to {action} {Name}");
}
private void Enter(User user)
{
_occupants.Add(user);
}
private void Open()
{
IsOpen = true;
}
public bool IsOpen { get; set; }
}
public interface IEventLog
{
void Add(IAction action, User user);
}
public class Enter : IAction
{
}
public interface IPropertyRules
{
bool UserAllowedTo(IAction action, User user, Property property);
}
public class Open : IAction
{
}
public class ActionResult
{
public ActionResult(bool successful, string why)
{
Successful = successful;
WhatHappened = why;
}
public bool Successful { get; private set; }
public string WhatHappened { get; private set; }
}
public interface IAction
{
}
public class User
{
}https://stackoverflow.com/questions/42821477
复制相似问题