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();
    }
}

今、私はファイルベースの履歴が欲しかった。コードをDRYに保つために、InMemoryHistoryから継承し、追加するたびにリスト全体を保持したいと思いました。

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メソッドが必要なことです。それほど大したことではありませんが、この状況により適したパターンが欠けているのではないかと思います。

アップデート

キースがすでに示唆したように。それはちょっと私を悩ませている継承アプローチでもあります。継承は常に問題である必要があります。

「FileBasedHistoryInMemoryHistoryです」とは言えません。

ですから、これにはStrategyPatternを使用する必要があるのではないかと思います。または、ロジックの一部を実装するが拡張の余地を残すAbstractConsoleを作成することもできます。それをリファクタリングする方法について何か提案はありますか?

4

3 に答える 3

2

リストをコンストラクターとして渡しているのは奇妙だと思います。そんなことをする必要はまったくありません...

GetHistoryFromFile を新しいリストを作成するものと考えるのではなく、既存のリストにロードする方法と考えてください (複数のファイルを履歴にロードできるため、より一般的には便利になります)。

また、ディスクへの書き込みでは、削除とクリアが正しく機能しません...

また、行ごとにディスクに書き込むと、かなり遅くなる可能性があります...

また、InMemory およびファイル ベースのストレージが同時結合の影響を受けている可能性があります。つまり、現時点では似ていますが、発散する可能性があります。たとえば、ディスク ベースのシステムがローリング ヒストリ ファイルとキャッシュされたヒストリを使用している場合などです。したがって、継承構造にとどまるために InMemory と File に執着しすぎないでください。それらを分解する方が簡単な場合があります。

于 2012-07-23T22:50:47.987 に答える
1

私はあなたがそれを完璧に持っていると思います。GetHistoryFromFileにのみ適用されるためFileBasedHistory、そこにある必要があることは理にかなっています。

于 2012-07-23T22:38:38.213 に答える
0

ここで使用できIteratorます。これらの 3 つの方法は、データの反復処理のみに使用されます。

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

そして、これらのメソッドはデータを管理するためのものです:

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

これは別の責任だと思います。私は彼らを別のクラスに移動しました。

于 2012-07-23T23:39:28.543 に答える