c# - 是否应该通过组合或其他方式引入新行为?

标签 c# polymorphism composition oop

我选择使用组合来公开一些新行为,而不是将新对象注入(inject)到我的消费者代码中或让消费者提供自己的新行为实现。我是否做出了错误的设计决策?

我有新的要求,即我需要仅在某些情况下实现一些特殊行为。我选择定义一个新接口(interface),在一个单独负责执行行为的具体类中实现新接口(interface)。最后,在消费者引用的具体类中,我实现了新接口(interface)并将其委托(delegate)给执行该工作的类。

以下是我正在使用的假设...

  • 我有一个名为 IFileManager 的接口(interface),它允许实现者管理各种类型的文件
  • 我有一个返回 IFileManager 的具体实现的工厂
  • 我有 3 个 IFileManager 实现,它们是(LocalFileManager、DfsFileManager、CloudFileManager)
  • 我有一个新要求,要求我只需要管理由 CloudFileManager 管理的文件的权限,因此管理权限的行为对于 CloudFileManager 是唯一的

这是引导我编写代码的测试...

[TestFixture]
public class UserFilesRepositoryTest
{
    public interface ITestDouble : IFileManager, IAclManager { }

    [Test]
    public void CreateResume_AddsPermission()
    {
        factory.Stub(it => it.GetManager("cloudManager")).Return(testDouble);

        repository.CreateResume();

        testDouble.AssertWasCalled(it => it.AddPermission());
    }

    [SetUp]
    public void Setup()
    {
        testDouble = MockRepository.GenerateStub<ITestDouble>();
        factory = MockRepository.GenerateStub<IFileManagerFactory>();
        repository = new UserFileRepository(factory);
    }

    private IFileManagerFactory factory;
    private UserFileRepository repository;
    private ITestDouble testDouble;
}

这是我设计的外壳(这只是基本轮廓,不是整个shibang)...

public class UserFileRepository
{
    // this is the consumer of my code...
    public void CreateResume()
    {
        var fileManager = factory.GetManager("cloudManager");
        fileManager.AddFile();

        // some would argue that I should inject a concrete implementation 
        // of IAclManager into the repository, I am not sure that I agree...
        var permissionManager = fileManager as IAclManager;
        if (permissionManager != null)
            permissionManager.AddPermission();
        else
            throw new InvalidOperationException();
    }

    public UserFileRepository(IFileManagerFactory factory)
    {
        this.factory = factory;
    }

    private IFileManagerFactory factory;
}

public interface IFileManagerFactory
{
    IFileManager GetManager(string managerName);
}

public class FileManagerFactory : IFileManagerFactory
{
    public IFileManager GetManager(string managerName)
    {
        IFileManager fileManager = null;
        switch (managerName) {
            case "cloudManager":
                fileManager = new CloudFileManager();
                break;
            // other managers would be created here...
        }

        return fileManager;
    }
}

public interface IFileManager
{
    void AddFile();
    void DeleteFile();
}

public interface IAclManager
{
    void AddPermission();
    void RemovePermission();
}

/// <summary>
/// this class has "special" behavior
/// </summary>
public class CloudFileManager : IFileManager, IAclManager
{
    public void AddFile() { 
        // implementation elided...
    }

    public void DeleteFile(){ 
        // implementation elided...
    }

    public void AddPermission(){
        // delegates to the real implementation
        aclManager.AddPermission();
    }

    public void RemovePermission() {
        // delegates to the real implementation
        aclManager.RemovePermission();
    }

    public CloudFileManager(){
        aclManager = new CloudAclManager();
    }

    private IAclManager aclManager;
}

public class LocalFileManager : IFileManager
{
    public void AddFile() { }

    public void DeleteFile() { }
}

public class DfsFileManager : IFileManager
{
    public void AddFile() { }

    public void DeleteFile() { }
}

/// <summary>
/// this class exists to manage permissions 
/// for files in the cloud...
/// </summary>
public class CloudAclManager : IAclManager
{
    public void AddPermission() { 
        // real implementation elided...
    }

    public void RemovePermission() { 
        // real implementation elided...
    }
}

最佳答案

您添加新行为的方法只为您节省了总体方案中的初始化,因为您实现了 CloudAclManagerCloudFileManager 分开无论如何。我不同意一些关于如何将其与您现有设计集成的事情(这还不错)...

这有什么问题吗?

  1. 您分离了文件管理器并使用 IFileManager ,但你没有对 IAclManager 做同样的事情。当您有一个工厂来创建各种文件管理器时,您会自动创建 CloudAclManager IAclManagerCloudFileManager 。那么,拥有IAclManager有什么意义呢? ?
  2. 更糟糕的是,你 初始化一个新的CloudAclManager CloudFileManager里面每次您尝试获取其 ACL 时 经理-你刚刚给了工厂 对你的责任 CloudFileManager .
  3. 您有CloudFileManager实现IAclManager除了将其作为属性(property)之外。您刚刚移动了权限对 CloudFileManager 唯一的规则进入模型层而不是业务规则层。这也导致支持不必要的 self 和 property 之间循环引用的潜力。
  4. 即使你愿意 CloudFileManager委托(delegate) 权限功能 CloudAclManager ,为什么要误导别人 类认为 CloudFileManager处理自己的 权限集?你刚刚做了你的 模型类看起来像一个门面。

好的,那么我应该做什么呢?

首先,您将类(class)命名为 CloudFileManager ,这是正确的,因为它唯一的职责是管理云文件。既然权限集也必须针对云进行管理,那么它真的适合CloudFileManager吗?承担这些新的责任?答案是否定的。

这并不是说您不能在同一个类中使用管理文件的代码和管理权限的代码。然而,将类命名为更通用的名称(例如 CloudFileSystemManager)会更有意义。因为它的职责不仅限于文件或权限。

不幸的是,如果您重命名您的类,则会对当前使用您的类的用户产生负面影响。那么,仍然使用组合,但不改变 CloudFileManager 怎么样? ?

我的建议是执行以下操作:

<强>1。保留您的IAclManager并创建IFileSystemManager

public interface IFileSystemManager {
    public IAclManager AclManager { get; }
    public IFileManager FileManager { get; }
}

public interface IFileSystemManager : IAclManager, IFileManager {

}

<强>2。创建CloudFileSystemManager

public class CloudFileSystemManager : IFileSystemManager {
    // implement  IFileSystemManager
    // 
    // How each manager is set is up to you (i.e IoC, DI, simple setters, 
    // constructor parameter, etc.).
    // 
    // Either way you can just delegate to the actual IAclManager/IFileManager
    // implementations.
}

为什么?

这将允许您在使用新行为时对当前代码库/功能的影响最小,而不会影响那些使用原始代码的人。文件管理和权限管理也可以同时进行(即在尝试实际文件操作之前检查权限)。如果您需要任何其他权限集管理器或任何其他类型的管理器,它也是可扩展的。

编辑 - 包括询问者的澄清问题

如果我创建IFileSystemManager : IFileManager, IAclManager ,存储库是否仍使用 FileManagerFactory 并返回 CloudFileSystemManager 的实例?

不,一个FileManagerFactory不应返回 FileSystemManager 。您的 shell 必须更新才能使用新的接口(interface)/类。也许像下面这样:

private IAclManagerFactory m_aclMgrFactory;
private IFileManagerFactory m_fileMgrFactory;

public UserFileRepository(IAclManagerFactory aclMgrFactory, IFileManagerFactory fileMgrFactory) {
    this.m_aclMgrFactory = aclMgrFactory;
    this.m_fileMgrFactory = fileMgrFactory;
}

public void CreateResume() {
    // I understand that the determination of "cloudManager"
    // is non-trivial, but that part doesn't change. For
    // your example, say environment = "cloudManager"
    var environment = GetEnvMgr( ... );

    var fileManager = m_fileMgrFactory.GetManager(environment);
    fileManager.AddFile();

    // do permission stuff - see below
}

至于调用要完成的权限,您有几个选择:

// can use another way of determining that a "cloud" environment
// requires permission stuff to be done
if(environment == "cloudManager") {
    var permissionManager = m_aclMgrFactory.GetManager(environment);
    permissionManager.AddPermission();
}

// assumes that if no factory exists for the environment that
// no permission stuff needs to be done
var permissionManager = m_aclMgrFactory.GetManager(environment);
if (permissionManager != null) {
    permissionManager.AddPermission();
}

关于c# - 是否应该通过组合或其他方式引入新行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4332331/

相关文章:

c++ - 智能指针的 const 正确性

python - 何时使用关联、聚合、组合和继承?

java - Java 中的组合

c# - 下载viewModel后如何调用函数?

c# - 如何使用 JSON.NET 检查 JSON 中缺少的字段

c# - 在主线程继续任务

java - 多态二叉搜索树

c++ - 我可以从基类指针访问派生类的非虚函数吗?

c# - 来自 Windows 窗体应用程序中 URI 的图像

java - 层和组件有什么区别?