把接口当作参数传入

1. 基础概念

  • 前段时间一直在看设计模式的基础概念,总结起来其实也就是一些老生常谈的各种原则和定义,初看这些原则和定义实来枯燥乏味,但是一番实践之后,却又发现它们简练而不失准确性,故贴在笔记开头,方便随时对照:
 - 目标:高内聚,低耦合/复用性高(一些相互关联的方法就应该放一个类里面,叫高内聚;类与类之间需要更少的依赖,叫做低耦合)
 - 设计原则:
    - 单一职责原则:一个类负责的东西需要专精
    - 依赖倒置原则:需要面向接口(抽象类)编程,而不是面向实现编程
    - 开闭原则:拓展原有代码的方式应该是在原有代码的基础上拓展,而不是直接修改原有的代码
    - 接口隔离原则:创造更多的专用接口,而不是用一个单一的总接口
    - 里氏替换原则:父类出现的地方都可以使用子类替代,且功能不会发生影响
    - 合成复用原则:尽力使用对象的组合/聚合(集合关系和组成关系),而不要过多的使用继承
    - 迪米特原则:交互的内容尽量小,影响才会最小(通过方法传参,通过字段,通过属性等等方式用来交互会比较好,比较清晰)

    开闭原则是目标,里氏转换原则是基础,依赖倒置原则是手段。
    隔离变换。
    针对接口编程,而不是针对实现编程。

2. 关于接口的探讨

  • 经过一段时间的OOP以及设计模式的训练之后,我逐渐习惯用接口隔离变化的思维方式:即把对象分类,用接口来区分不同类的区别(它们可能需要实现的东西是一样的,但是具体实现的方法是不同的,比如对模块写入电压模式或者电阻模式,其实都是写入模式<方法名>,但是写入的具体数据内容和长度那是完全不同的<方法体>)
  • 和继承相比,虽然父类也可以用virtual这种虚方法,再在子类中ovrride来实现。但是父类毕竟不如接口灵活(一个接口就封装一类变化,但是一个父类用来封装一组变化始终感觉比较奇怪);另外接口不需要继承,只需要实现,所以可以对需要实现接口的类细分出好多种不同的接口,程序的灵活性又+1.
  • 还是原则上提的:针对接口编程,而不是针对实现编程。,父类老老实实当一个abstract的容器最好,OOP上多用接口,少用继承。(一个父类可以被好多个子类继承,那么在实现的时候,就可以通过开闭原则来使得程序更加灵活,毕竟可以一对多实现(当然,接口也有这样的特点,甚至比继承更灵活))。
  • 说到一对多实现这一点,这里就多说一点关于简单工厂,反射工厂,抽象工厂这几种最常用的设计模式:
    • 简单工厂:它也不属于设计模式,它的简单原理就是通过switch..case of来判定当前输入的Enum元素是哪个,判断出哪个就返回哪个,灵活性+1;
    • 反射工厂,它在基本原来上和简单工厂一样,但是加入一些语法糖(比如C#的default(T) = typeof()这种反射机制),省略switch的硬编码,使得程序更加简单易懂。
    • 抽象工厂,这就是利用抽象父类当个容器,你想要实现实现哪个子类你就在构造函数中以哪个子类来创建对象,反正抽象父类类型可以对应所有的实例子类。

3. 好多地方喜欢把接口当作参数传入的原因?

  • 最开始我的理解如上,接口就是用来隔离变化,以更好的override变化。所以每次如果需要实现某种接口的时候,我就直接实现接口对应的类就好了。
  • 但是:如果想更加灵活的实现接口(比如我写了很多个类,每一个类都继承了同一个接口,但这个时候这些类是没有一个统一的父类的,做不到一对多(里氏转换原则)),这个时候就需要把接口当作一个共同的类型,你只要知道调用时接口是通过哪个类实现的,你就可以实现灵活的调用接口
  • 看代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace test
{
    //定义接口
    public interface ILogger
    {
        void Log(string message);
    }
    //实现接口的类
    public class ConsoleLogger : ILogger
    {
        public void Log(string message)
        {
            Console.WriteLine(message);
        }
    }
    //使用接口作为参数传递的方法
    public class Processer
    {
        private readonly ILogger _logger;
        public Processer(ILogger logger)//从构造函数处实现接口的传入
        {
            _logger = logger;
        }
        public void Process()//使用接口的方法,但在真实调用之前这些方法都是虚的
        {
            _logger.Log("abcd");
            _logger.Log("efgh");
        }
    }
    //调用过程,此时才真正给接口分配真实实现方法
    public class Program
    {
        public static void Main(string[] args)
        {
            ILogger logger = new ConsoleLogger();//此处真正关联接口和接口的实现
            Processer processer = new Processer(logger);//传入已经实例后的接口
            processer.Process();//调用实现方法
        }
    }
}
posted @ 2024-10-17 14:14  不愿透露姓名的小村村  阅读(20)  评论(0编辑  收藏  举报