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();//调用实现方法
}
}
}