0

我有以下界面

interface IConsoleHistory
{
    void Add(string entry);

    HistoryEntry GetNextEntry();

    HistoryEntry GetPreviousEntry();

    void ResetHistoryMarker();

    void Delete(HistoryEntry entry);

    void DeleteEntireHistory();
}

public class HistoryEntry
{
    public HistoryEntry(string value, int index, bool isCommand)
    {
        Value = value;
        Index = index;
        IsCommand = isCommand;
    }

    public string Value { get; private set; }

    public int Index { get; private set; }

    public bool IsCommand { get; private set; }
}

基于此,我实现了一个 InMemoryHistory:

public class InMemoryHistory : IConsoleHistory
{
    protected List<string> History { get; private set; }
    private int _currentIndex;

    public InMemoryHistory() :this(new List<string>())
    {
    }

    protected InMemoryHistory(List<string> history)
    {
        History = history;
        _currentIndex = -1;
    }

    public virtual void Add(string entry)
    {
        History.Insert(0, entry);
    }

    public HistoryEntry GetNextEntry()
    {
        if (GetHighestIndex() > _currentIndex)
        {
            _currentIndex++;
            return ReturnAtIndex(_currentIndex);
        }

        return null;
    }

    private int GetHighestIndex()
    {
        return History.Count - 1;
    }

    private int GetLowestIndex()
    {
        return History.Count > 0 ? 0 : -1;
    }

    public HistoryEntry GetPreviousEntry()
    {
        if (_currentIndex > GetLowestIndex())
        {
            _currentIndex--;
            return ReturnAtIndex(_currentIndex);
        }
        _currentIndex = -1;
        return null;
    }

    private HistoryEntry ReturnAtIndex(int index)
    {
        return new HistoryEntry(History[index], index, false);
    }

    public void ResetHistoryMarker()
    {
        _currentIndex = -1;
    }

    public void Delete(HistoryEntry entry)
    {
        if (History.ElementAtOrDefault(entry.Index) != null)
        {
            History.RemoveAt(entry.Index);
        }
    }

    public void DeleteEntireHistory()
    {
        History.Clear();
    }
}

现在我想要一个基于文件的历史记录。为了保持代码干燥,我想从 InMemoryHistory 继承,并在每次添加后保留整个 List。

public class FileBasedHistory : InMemoryHistory
{
    private readonly string _fileName;

    public FileBasedHistory():this("history.txt")
    {
    }

    public FileBasedHistory(string fileName) :base(GetHistoryFromFile(fileName))
    {
        _fileName = fileName;
    }

    public override void Add(string entry)
    {
        base.Add(entry);
        WriteToDisk();
    }

    private void WriteToDisk()
    {
        using(var textWriter = new StreamWriter(_fileName, false, Encoding.UTF8))
        {
            History.ForEach(textWriter.WriteLine);
        }
    }

    private static List<string> GetHistoryFromFile(string fileName)
    {
        if (!File.Exists(fileName))
            return new List<string>();

        return File
            .ReadAllLines(fileName)
            .ToList();
    }
}

这就像一个魅力。困扰我的是我需要静态GetHistoryFromFile方法。这并不是什么大问题,但我想知道我是否错过了更适合这种情况的模式?

更新

正如基思已经建议的那样。这也是让我有点困扰的继承方法。继承应该始终是is a的问题。

你不能说:“A FileBasedHistory is a InMemoryHistory”

所以我想知道我是否应该尝试为此使用StrategyPattern。或者可能编写一个 AbstractConsole 来实现部分逻辑但为扩展留出空间。关于如何重构它的任何建议?

4

3 回答 3

2

我觉得很奇怪您将列表作为构造函数传递。你根本不必那样做...

与其将您的 GetHistoryFromFile 视为创建新列表,不如将其视为加载到现有列表中的一种方法(这种方式也变得更普遍有用......因为它可以将多个文件加载到历史记录中)。

删除和清除也无法正常写入磁盘...

同样逐行写入磁盘可能会变得很慢......

此外,您的 InMemory 和基于文件的存储可能会遇到巧合耦合。这意味着虽然它们目前相似,但它们可能有分歧的机会。例如,如果您的基于磁盘的系统使用滚动历史文件和缓存历史。所以不要太依赖 InMemory 和 File 以保持在继承结构中,将它们分开可能更容易

于 2012-07-23T22:50:47.987 回答
1

我认为你已经做到了完美。GetHistoryFromFile仅适用于 a FileBasedHistory,因此它应该在那里是有道理的。

于 2012-07-23T22:38:38.213 回答
0

你可以Iterator在这里使用。这三种方法仅用于迭代数据:

HistoryEntry GetNextEntry();
HistoryEntry GetPreviousEntry();
void ResetHistoryMarker();

这些方法用于管理数据:

void Add(string entry);
void Delete(HistoryEntry entry);
void DeleteEntireHistory();

我认为这是一项不同的职责,我将他们转移到不同的班级。

于 2012-07-23T23:39:28.543 回答