1.依赖倒置原则:(DIP)高层模块不应依赖底层模块,两者都应依赖抽象。
为什么依赖注入? 为了实现控制反转.
为什么控制反转? 我们的软件设计需要符合依赖倒置原则.
2.依赖注入(DI) 控制反转(IOC)(技术手段)
·IOC容器,依赖注入,IOC控制反转的关系
依赖注入,反转依赖的!因为有了依赖注入,才有了控制反转;
即先有容器再有依赖注入,最后才有控制反转。
IOC容器,依赖注入框架的东西提供的。映射依赖、管理对象创建和生命周期;再也不用New,有人帮你new。
场景例子:假设你是一个五岁的小孩,你饿了就要吃,需求。怎么找吃的?
1.找吃的 直接从冰箱拿
2.找妈妈要 父母(IOC容器)会给你做食物,然后拿给你(注入)
从你想吃东西的需求,从主动去冰箱(正转)拿食物,到想吃的食物而被动获得(反转),即妈妈做
控制反转:组件对象控制权的转移。类型A(小孩)中需要使用类型B(食物),这个B的创建并不由A来负责,而是由第三方(妈妈)创建。
IOC一种反转流、依赖和接口的方式,他把传统上由程序代码直接操控的对象的调用权交给第三方(容器),通过第三方来实现对象组件的配置和管理。
场景:父亲给孩子讲故事,只要给父亲一本书,他就可以照着书给孩子将故事。
1.不经过任何设计模式和设计原则加工的传统的OOP方式,强依赖,以利于扩展。
namespace FatherStoryCustom
{
/// <summary>
/// 场景:父亲给孩子讲故事,只要给父亲一本书,他就可以照着书给孩子将故事。
///
/// 不经过任何设计模式和设计原则加工的传统的OOP方式。。
/// </summary>
class Program
{
static void Main(string[] args)
{
Father father = new Father();
father.Read();
Console.ReadKey();
}
}
public class Book
{
public string GetContent()
{
return "从前有座山,山上有座庙.....";
}
}
public class Father
{
public void Read()
{
Book book = new Book();
Console.WriteLine("爸爸开始讲故事啦..");
Console.WriteLine(book.GetContent());
}
}
}
场景:父亲给孩子讲故事,只要给父亲一本书,他就可以照着书给孩子将故事。
有一天需求变了,不给书了,给报纸,读报纸上的新闻。
2.通过工厂模式来实现,但还是需要在工厂中创建对象
namespace FatherStoryDIP
{
/// <summary>
/// 场景:父亲给孩子讲故事,只要给父亲一本书,他就可以照着书给孩子将故事。
/// 有一天需求变了,不给书了,给报纸,读报纸上的新闻。
/// 上一个普通做法耦合太强,父亲太依赖书了。
/// </summary>
class Program
{
static void Main(string[] args)
{
// 工厂模式创建对象
Father father = new Father("Paper");
Console.ReadKey();
}
}
/// <summary>
/// 读物的接口
/// </summary>
public interface IReader
{
string GetContent();
}
public class Paper : IReader
{
public string GetContent()
{
return "王思聪被限制消费..";
}
}
public class Book : IReader
{
public string GetContent()
{
return "从前有座山,山上有座庙...";
}
}
public class Father
{
private IReader _reader { get; set; }
// 工厂模式创建对象
public Father(string readerName)
{
_reader = ReaderFactory.GetReader(readerName);
}
public void Read()
{
Console.WriteLine("爸爸给孩子开始讲故事了..");
Console.WriteLine(_reader.GetContent());
}
}
}
namespace FatherStoryDIP
{
public static class ReaderFactory
{
public static IReader GetReader(string readType)
{
if (string.IsNullOrEmpty(readType))
{
return null;
}
switch (readType)
{
case "Paper":
return new Paper();
case "Book":
return new Book();
default:
return null;
}
}
}
}
3.和上面2的场景一样,通过依赖注入来创建对象。将上面2的代码进行再升级。
工厂模式 =>升级=> IOC容器的基础
IOC容器,一个自动化的工厂,还带配送,要什么给什么。彻底的解耦,高层不依赖于低层,依赖抽象。
namespace FatherStoryDIP
{
/// <summary>
/// 场景:父亲给孩子讲故事,只要给父亲一本书,他就可以照着书给孩子将故事。
/// 有一天需求变了,不给书了,给报纸,读报纸上的新闻。
/// </summary>
class Program
{
static void Main(string[] args)
{
// 通过容器创建对象
var sc = new ServiceCollection();
// 接口对应实例进行注册
//sc.AddScoped(typeof(IReader), typeof(Book));
//var sp = sc.BuildServiceProvider();
//IReader reader = sp.GetService<IReader>();
//Father father = new Father(reader);
//father.Read();
// Unity创建容器 全程没有new对象
var container = new UnityContainer();
container.RegisterType<IReader, Book>();
container.RegisterType<Father>();
// 解析容器中的对象,用什么泛型中填什么
var father = container.Resolve<Father>();
father.Read();
Console.ReadKey();
}
}
/// <summary>
/// 读物的接口
/// </summary>
public interface IReader
{
string GetContent();
}
public class Paper : IReader
{
public string GetContent()
{
return "王思聪被限制消费..";
}
}
public class Book : IReader
{
public string GetContent()
{
return "从前有座山,山上有座庙...";
}
}
public class Father
{
private IReader _reader { get; set; }
public Father(IReader reader)
{
_reader = reader;
}
public void Read()
{
Console.WriteLine("爸爸给孩子开始讲故事了..");
Console.WriteLine(_reader.GetContent());
}
}
}
容器类似于一个登记本,生产的东西登记到容器中,用的时候直接从容器中取。
当前代码用硬编码的方式注册,注册类型的方式有很多种
1.配置文件,动态加载
2.批量注册,将特殊标识的实体类(例:后缀为Service或者有某个特性)的全部注册到容器中
3.通过反射,扫描程序集,找到你想要找到的类注册进来。例如后缀名为Service