1 介绍

SOLID 设计原则实际上是 Robert C. Martin 提出的五个设计原则。它们分别是:Single Responsibility Principle(单一责任原则)、Open-Close Principle(开闭原则)、Liskov Substitution Principle(里氏替换原则)、Interface Segregation Principle(接口分离原则)、Dependency Inversion Principle(依赖倒置原则)。

2 单一责任原则

看以下代码:

public class Journal
{
    private readonly List<string> entries = new List<string>();

    private static int count = 0;

    public int AddEntry(string text)
    {
        entries.Add($"{++count}: {text}");
        return count;
    }

    public void RemoveEntry()
    {
        entries.RemoveAt(count - 1);
        count--;
    }

    public override string ToString()
    {
        return string.Join(Environment.NewLine, entries);
    }

    public void Save(string filename)
    {
        File.WriteAllText(filename, ToString());
    }
}

这是一个日记类,我们可以不断往其中添加语句,删除语句,或者输出日记内容。但是我们看到最后一个 Save 方法,它不仅要负责维护自己的私有字段,还要负责自己的持久化,这意味着 Journal 类承担了太多的责任,如果我们想修改 entries 的管理方式,我们要修改这个类,我们想要修改数据的储存方式,还是要修改这个类,这样我们就为修改这个类带来了多个理由。

当然,这种拆分来自于直觉,到底哪些功能属于一类,这个需要多加练习和思考,不一定能一步到位,比如说我们把这个类想象成 MVC 中的 Model,那我们就知道,这个类应该只负责存储和管理 entries,当我们要将这个 model 中的数据 entries 封入协议或者输出到文件中,这样的工作应该是在 Controller 中完成,这样分析我们就能想到持久化的工作并不应该交给 Journal 本身来做。

public class Persistence
{
    public void SaveToFile(Journal journal, string filename, bool overwrite = false)
    {
        if (overwrite || File.Exists(filename))
        {
            File.WriteAllText(filename, journal.ToString());
        }
    }
}

Main 函数中的调用如下:

var journal = new Journal();
journal.AddEntry("I cried today");
journal.AddEntry("I ate a bug");
Console.WriteLine(journal);

var persistence = new Persistence();
const string filename = @"../../journal.txt";
persistence.SaveToFile(journal, filename, true);

目前来看 Persistence 依赖于 Journal,只是为了简化代码,在实际工作中应该是传入接口。

3 开闭原则

看下面的代码:

public class Product
{
    public string Name { get; set; }
    public Color Color { get; set; }
    public Size Size { get; set; }

    public Product(string name, Color color, Size size)
    {
        Name = name ?? throw new ArgumentNullException(nameof(name));
        Color = color;
        Size = size;
    }
}

public enum Size
{
    Small,
    Medium,
    Large,
    Huge
}

public enum Color
{
    Red,
    Green,
    Blue
}
public class ProductFilter
{
    public IEnumerable<Product> FilterBySize(IEnumerable<Product> products, Size size)
    {
        foreach (var product in products)
        {
            if (product.Size == size)
            {
                yield return product; // yield return 一次返回一个元素 然后保存现场 再次调用时会从现场继续执行
            }
        }
    }

    public IEnumerable<Product> FilterByColor(IEnumerable<Product> products, Color color)
    {
        foreach (var product in products)
        {
            if (product.Color == color)
            {
                yield return product; 
            }
        }
    }

    public IEnumerable<Product> FilterByColorAndSize(IEnumerable<Product> products, Color color, Size size)
    {
        foreach (var product in products)
        {
            if (product.Color == color && product.Size == size)
            {
                yield return product; 
            }
        }
    }
}