美文网首页
{C#} 非里氏替换的合并

{C#} 非里氏替换的合并

作者: 码农猫爸 | 来源:发表于2023-08-12 15:30 被阅读0次

背景

  • 处理有异同项的类是难点
  • 同项可继承或拥有,如下文学生类和工人类
  • 趁早合并向上传参,可减少代码的复杂度

汇总

  • 抽象:差异类,共享构造+独立方法
  • 泛型:差异类,共享构造+方法,对抽象改进
  • 继承:合并类,无泛型传参,对泛型改进
  • 接口:合并接口
  • 拥有:合并类,非继承公有类,语义更清晰

继承人员

using System.Diagnostics;

namespace NotLiskov
{
    // 抽象基类
    abstract class Person
    {
        public string Name { get; }
        public char Sex { get; }

        protected Person(string name, char sex)
        {
            Debug.Assert(!string.IsNullOrEmpty(name));
            Debug.Assert(sex == 'F' || sex == 'M');

            Name = name;
            Sex = sex;
        }
    }

    // 学生子类
    class Student : Person
    {
        public string Subject { get; } // 独有项

        public Student(string name, char sex, string subject)
            : base(name, sex)
        {
            Debug.Assert(!string.IsNullOrEmpty(subject));
            Subject = subject;
        }
    }

    // 工人子类
    class Worker : Person
    {
        public int Salary { get; } // 独有项

        public Worker(string name, char sex, int salary)
            : base(name, sex)
        {
            Debug.Assert(salary > 0);
            Salary = salary;
        }
    }
}

拥有人员

using System.Diagnostics;

namespace NotLiskov
{
    // 非基类
    class Solo
    {
        public string Name { get; }
        public char Sex { get; }

        public Solo(string name, char sex)
        {
            Name = name;
            Sex = sex;
        }
    }

    class SoloStudent
    {
        public Solo Person { get; } // 共有项,拥有而非继承
        public string Course { get; }

        public SoloStudent(Solo p, string course)
        {
            Debug.Assert(p != null);
            Debug.Assert(!string.IsNullOrEmpty(course));

            Person = p;
            Course = course;
        }
    }

    class SoloWorker
    {
        public Solo Person { get; } // 共有项,拥有而非继承
        public int Salary { get; }

        public SoloWorker(Solo p, int salary)
        {
            Debug.Assert(p != null);
            Debug.Assert(salary > 0);

            Person = p;
            Salary = salary;
        }
    }
}

抽象

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace NotLiskov
{
    // - 类内泛型T,类内有效,不能采用ctor<T>
    // - T不同时,类不同
    // - 向上传参时,仍需明确T,不利传参
    abstract class AbstractCalculator<T>
    {
        protected readonly List<T> _people;

        // 共享构造
        public AbstractCalculator(List<T> people)
        {
            Debug.Assert(people.Count > 0);
            _people = people;
        }

        public abstract int Count(char sex);
    }

    class WorkerAbstractCalculator : AbstractCalculator<Worker>
    {
        public WorkerAbstractCalculator(List<Worker> workers) : base(workers) { }

        public override int Count(char sex)
            => _people.Count(x => x.Sex == sex);
    }

    class StudentAbstractCalculator : AbstractCalculator<Student>
    {
        public StudentAbstractCalculator(List<Student> students) : base(students) { }

        public override int Count(char sex)
            => _people.Count(x => x.Sex == sex);
    }
}

泛型

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace NotLiskov
{
    // - T: someClass,限定类或其子类
    class GernericCalculator<T> : ICalculator where T : Person
    {
        readonly List<T> _people;

        // 共享构造
        public GernericCalculator(List<T> people)
        {
            Debug.Assert(people.Count > 0);
            _people = people;
        }

        // 共享方法
        public int Count(char sex)
            => _people.Count(x => x.Sex == sex);
    }
}

继承

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace NotLiskov
{
    class Calculator
    {
        // List<T>支持不变,输入类型必须完全相等
        // IEnumerable<T>支持协变
        readonly IEnumerable<Person> _people;

        public Calculator(IEnumerable<Person> people)
        {
            Debug.Assert(people.Count() > 0);
            _people = people;
        }

        public int Count(char sex)
            => _people.Count(x => x.Sex == sex); // 提升为基类后,调用共用属性
    }
}

接口

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace NotLiskov
{
    interface ICalculator
    {
        int Count(char sex);
    }

    // 继承接口,但需分别处理构造方法
    // - 实现异类合并
    // - 向上传参时,无需构造函数

    class WorkerCalculator : ICalculator
    {
        readonly List<Worker> _workers;

        public WorkerCalculator(List<Worker> workers)
        {
            Debug.Assert(workers.Count > 0);
            _workers = workers;
        }

        public int Count(char sex)
            => _workers.Count(x => x.Sex == sex);
    }

    class StudentCalcutor : ICalculator
    {
        readonly List<Student> _students;

        public StudentCalcutor(List<Student> students)
        {
            Debug.Assert(students.Count > 0);
            _students = students;
        }

        public int Count(char sex)
            => _students.Count(x => x.Sex == sex);
    }
}

拥有

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace NotLiskov
{
    class SoloCalculator
    {
        readonly List<Solo> _people;

        public SoloCalculator(List<Solo> people)
        {
            Debug.Assert(people.Count() > 0);
            _people = people;
        }

        public int Count(char sex)
            => _people.Count(x => x.Sex == sex); // 提升为基类后,调用共用属性
    }
}

客户代码

using System.Collections.Generic;
using System.Linq;
using static System.Console;

namespace NotLiskov
{
    class Program
    {
        static void Main(string[] args)
        {
            var workers = new List<Worker>
            {
                new Worker("张三", 'M', 8000),
                new Worker("李四", 'M', 7000),
            };
            var students = new List<Student>
            {
                new Student("王五", 'M', "Chinese"),
                new Student("小六", 'F', "Math"),
            };
            var soloWorkers = new List<SoloWorker>
            {
                new SoloWorker(new Solo("张三", 'M'), 8000),
                new SoloWorker(new Solo("李四", 'M'), 7000),
            };
            var soloStudents = new List<SoloStudent>
            {
                new SoloStudent(new Solo("王五", 'M'), "Chinese"),
                new SoloStudent(new Solo("小六", 'F'), "Math"),
            };

            // [1#]抽象,实例异类
            WriteLine("\n----抽象");
            AbstractCalculator<Worker> a1 = new WorkerAbstractCalculator(workers);
            WriteLine($"工人数(男): {a1.Count('M')}");
            AbstractCalculator<Student> a2 = new StudentAbstractCalculator(students);
            WriteLine($"学生数(男): {a2.Count('M')}");

            // [2#]泛型,实例异类
            WriteLine("\n----泛型");
            var g1 = new GernericCalculator<Worker>(workers);
            WriteLine($"工人数(男): {g1.Count('M')}");
            var g2 = new GernericCalculator<Student>(students);
            WriteLine($"学生数(男): {g2.Count('M')}");

            // [3#]继承,实例同类
            WriteLine("\n----继承");
            var c = new Calculator(workers);
            WriteLine($"工人数(男): {c.Count('M')}");
            c = new Calculator(students);
            WriteLine($"学生数(男): {c.Count('M')}");

            // [4#]接口,实例同接口
            WriteLine("\n----接口");
            ICalculator i = new WorkerCalculator(workers);
            WriteLine($"工人数(男): {i.Count('M')}");
            i = new StudentCalcutor(students);
            WriteLine($"学生数(男): {i.Count('M')}");

            // [5#]拥有,实例同类
            WriteLine("\n----拥有");
            var s = new SoloCalculator(
                soloWorkers.Select(x => x.Person).ToList());
            WriteLine($"工人数(男): {s.Count('M')}");
            s = new SoloCalculator(
                soloStudents.Select(x => x.Person).ToList());
            WriteLine($"学生数(男): {s.Count('M')}");
        }
    }
}

相关文章

网友评论

      本文标题:{C#} 非里氏替换的合并

      本文链接:https://www.haomeiwen.com/subject/cvmxmdtx.html