【问题标题】:Subclassing all classes in a 'chain' of subclasses在子类“链”中对所有类进行子类化
【发布时间】:2013-07-25 17:32:39
【问题描述】:

我想出了这个例子来澄清我的问题

我们从一个基类开始

/// <summary>
/// Just a silly example class
/// </summary>
class CFileStream
{
    protected readonly string FilePath;

    public CFileStream(string filePath)
    {
        FilePath = filePath;
    }

    public virtual void Write(string s)
    {
        var stream = GetStream(FilePath);
        //etc
    }

    /// <summary>
    /// Take filePath as an argument to make subclassing easier
    /// </summary>
    protected virtual FileStream GetStream(string filePath)
    {
        return new FileStream(filePath, FileMode.OpenOrCreate);
    }
}

为它创建一个子类

/// <summary>
/// Building on top of CFileStream, created an encrypted version
/// </summary>
class CFileStreamEncrypted : CFileStream
{
    private readonly string _key;

    public CFileStreamEncrypted(string filePath, string key):base(filePath)
    {
        _key = key;
    }

    /// <summary>
    /// For added complexity, let's also wrap a possible excepton
    /// </summary>
    public override void Write(string s)
    {
        try
        {
            base.Write(s);
        }
        catch (ImaginaryCryptoException ex)
        {
            throw new ImaginaryCustomException("bladibla", ex);
        }
    }

    /// <summary>
    /// Wrap the base stream in an imaginary crypto class
    /// </summary>
    protected override FileStream GetStream(string filePath)
    {
        return new CImaginaryCryptoStream(base.GetStream(filePath), _key);
    }
}

现在我们希望创建第二个子类,但它可以与初始文件编写器以及加密版本一起使用。

第一个有道理

/// <summary>
/// Building on top of CFileStream, created an auto-split version
/// </summary>
class CFileStreamSplit : CFileStream
{
    public CFileStreamSplit(string filePath) 
        : base(filePath)
    {
    }

    protected int Counter;

    /// <summary>
    /// Close stream and move to next file at the appropriate time(s)
    /// </summary>
    public override void Write(string s)
    {
        do
        {
            Stream stream;
            if (ImaginaryBooleanMustSplit)
                stream = GetStream(FilePath);
            //etc
        } while (ImaginaryBooleanDataLeftToWrite);
    }

    /// <summary>
    /// Get base stream but with altered filePath
    /// </summary>
    protected override FileStream GetStream(string filePath)
    {
        return base.GetStream(GetNextpath(filePath));
    }

    /// <summary>
    /// Ignore proper extension / file-exists etc.
    /// </summary>
    protected virtual string GetNextpath(string filePath)
    {
        return filePath + ++Counter;
    }
}

第二个(下面这个)是完全重复的代码,除了现在还需要加密密钥的构造函数。

/// <summary>
/// Build the same auto-split version but this time on top of the encrypted subclass
/// </summary>
class CFileStreamSplitEncrypted : CFileStreamEncrypted
{
    public CFileStreamSplitEncrypted(string filePath, string key)
        : base(filePath, key)
    {
    }

    /*
     * Note that there are no changes below this line
     */

    protected int Counter;

    /// <summary>
    /// Close stream and move to next file at the appropriate time(s)
    /// </summary>
    public override void Write(string s)
    {
        do
        {
            Stream stream;
            if (ImaginaryBooleanMustSplit)
                stream = GetStream(FilePath);
            //etc
        } while (ImaginaryBooleanDataLeftToWrite);
    }

    /// <summary>
    /// Get base stream but with altered filePath
    /// </summary>
    protected override FileStream GetStream(string filePath)
    {
        return base.GetStream(GetNextpath(filePath));
    }

    /// <summary>
    /// Ignore proper extension / file-exists etc.
    /// </summary>
    protected virtual string GetNextpath(string filePath)
    {
        return filePath + ++Counter;
    }
}

当然有很多方法可以减少重复代码的数量,但我还没有找到“最好的”方法,如果有的话。所以;在您看来/经验中解决这个问题的最省时、最干净、最灵活的方法是什么?

【问题讨论】:

  • 请在投票结束时详细说明

标签: c# inheritance multiple-inheritance


【解决方案1】:

对于不同的修改,一个不错的方法可能是组合而不是继承。将你的类设置为只负责一件事,接受构造的基本流。

interface ICFileStream
{
    void Write(string s);
    FileStream GetStream(string filePath);
}

/// <summary>
/// Just a silly example class
/// </summary>
class CFileStream: ICFileStream
{
    protected readonly string FilePath;

    public CFileStream(string filePath)
    {
        FilePath = filePath;
    }

    public void Write(string s)
    {
        var stream = GetStream(FilePath);
        //etc
    }

    /// <summary>
    /// Take filePath as an argument to make subclassing easier
    /// </summary>
    protected FileStream GetStream(string filePath)
    {
        return new FileStream(filePath, FileMode.OpenOrCreate);
    }
}

/// <summary>
/// Building on top of CFileStream, created an encrypted version
/// </summary>
class CFileStreamEncrypted : ICFileStream
{
    private readonly string _key;
    private readonly ICFileStream _stream;

    public CFileStreamEncrypted(string key, ICFileStream stream)
    {
        _key = key;
        _stream = stream;
    }

    /// <summary>
    /// For added complexity, let's also wrap a possible excepton
    /// </summary>
    public void Write(string s)
    {
        try
        {
            _stream.Write(s);
        }
        catch (ImaginaryCryptoException ex)
        {
            throw new ImaginaryCustomException("bladibla", ex);
        }
    }

    /// <summary>
    /// Wrap the base stream in an imaginary crypto class
    /// </summary>
    protected FileStream GetStream(string filePath)
    {
        return new CImaginaryCryptoStream(_stream.GetStream(filePath), _key);
    }
}

class CFileStreamSplit : ICFileStream
{
    private readonly ICFileStream _stream;

    public CFileStreamSplit(ICFileStream stream) 
    {
        _stream = stream;
    }

    protected int Counter;

    /// <summary>
    /// Close stream and move to next file at the appropriate time(s)
    /// </summary>
    public void Write(string s)
    {
        do
        {
            Stream stream;
            if (ImaginaryBooleanMustSplit)
                stream = GetStream(FilePath);
            //etc
        } while (ImaginaryBooleanDataLeftToWrite);
    }

    /// <summary>
    /// Get base stream but with altered filePath
    /// </summary>
    protected FileStream GetStream(string filePath)
    {
        return _stream.GetStream(GetNextpath(filePath));
    }

    /// <summary>
    /// Ignore proper extension / file-exists etc.
    /// </summary>
    protected string GetNextpath(string filePath)
    {
        return filePath + ++Counter;
    }
}

所以当你想要一个拆分加密文件流时:

new  CFileStreamSplit(new CFileStreamEncrypted("crypto-awesome-key", new CFileStream("C:\\blah...")));

这更加灵活,因此当您想要添加 LoggingCFileStream 时,您不需要为每个组合添加单独的类。

【讨论】:

  • 谢谢,我曾考虑使用接口作为基础,因为它绝对更简洁,但组合而不是继承的“问题”意味着在重构现有代码时需要进行更多更改,但我想这是一个权衡减少重复代码
  • 哦,顺便说一句,这样做也意味着将受保护的方法 (GetStream) 公开,这是我想避免的……有什么想法吗?
猜你喜欢
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多