耦合是如何产生的?
一个类,往往需要跟其他对象打交道,既包括获知其他对象的状态,也包括依赖其他对象的行为,而一旦这样的事情发生时,我们便称该类依赖于另一个对象。只要两个对象之间存在一方依赖另一方的关系,那么我们就称这两个对象之间存在耦合。
解除依赖的思想是如何产生的?
(1)原始社会里,没有社会分工。须要斧子的人(调用者)仅仅能自己去磨一把斧子(被调用者)。相应的情形为:软件程序里的调用者自己创建被调用者。
(2)进入工业社会,工厂出现。斧子不再由普通人完毕,而在工厂里被生产出来,此时须要斧子的人(调用者)找到工厂,购买斧子,无须关心斧子的制造过程。相应软件程序的简单工厂的设计模式。
(3)进入“按需分配”社会,需要斧子的人不需要找到工厂,坐在家里发出一个简单指令:须要斧子。斧子就自然出如今他面前。依赖注入。
从(1)、(2)可以看出,不同点是解放了自己工作,不需要去磨这个斧子了,但是相同点就是这个主导者,还依然是自己。就说你自己磨斧子还是自己去工厂里去取。这个主要调用者当事人还是自己。
从(2)(3)可以看出,相同点,我们不需要去磨这个斧子了。不同点,就是主导者变了,斧子已经不需要我们去取了,而是我们想要什么就去要什么。就是控制权已经改变了,他已经不是我们自己去控制的了。
解耦
即让每一个部分都相互独立,一个部分的改变不会影响其他部分。
项目代码松耦合状态
常见的解耦方式
- DI(Dependency Injection),对依赖的解耦。
- aop(Aspect Oriented Programming),对关注切面关注点业务逻辑的解耦。处理的不是业务逻辑的逻辑,比如,日志、事务、缓存、异常处理、性能优化,这些逻辑应该是来服务我们的对象,而不是封装在公共行为里面。
- Dto(Data Transfer Object),对模型的解耦。数据传输对象DTO本身并不是业务对象。数据传输对象是根据UI的需求进行设计的,而不是根据领域对象进行设计的。
- ddd领域驱动设计,对领域业务模块的解耦。
DI(Dependency Injection),对依赖的解耦。
相关基本概念
类和对象
类:类是一个静态的概念,类本身不携带任何数据。当没有为类创建任何对象时,类本身不存在于内存空间中。
对象new实例化的过程:声明引用;
使用new关键字创建类的对象并对其初始化;(分配内存空间)将引用指向类的对象。
控制反转 IoC(Inversion of Control)
举例场景:
比如上面的例子(3)(工厂伐木),从自己需要去工厂买个斧子,然后我们拿着斧子去上山伐木,变成甲方会给我们提供斧子,我们只需要在家里准备好去就行了,这个时候,我们对斧头这个控制权,已经从我们自己悄悄转化到调用者甲方身上了。这就是控制反转的思想。
比如你把程序里的一个写死的变量改成从配置文件里读取也是一种控制反转(由程序控制反转为由框架控制),你把这个配置改成用户UI界面的一个输入文本框由用户输入也是一种控制反转(由框架控制反转为由用户自己控制)。这些都用了控制反转思想。
什么是控制反转IoC(Inversion of Control)?
是一种设计原则,最早由Martin Fowler提出,因为其理论提出时间和成熟时间相对较晚,所以并没有被包含在GoF的《设计模式》中。
对依赖的控制,从自己,转到自己的调用者上。
依赖注入 DI(Dependency Injection)
依赖注入实现了new(做了new实例化这个动作),不是为了省一个new!
依赖注入实现IoC的其中一种设计方法。依赖的对象注入到调用者,你不应该自己创建它,而是应该通过构造函数,由你的调用者给你——容器。
举例场景——自动售货机
容器是一个自动售货机,组件是放在里面的在售商品,服务是商品的出售名称。
把商品(项目里的具体对象)放入自动售货机(容器)上架的过程叫注册;
注册的时候会给商品贴上标签,标注该商品的名称,这个名称就叫服务;
我们还可以标注这个商品的适用人群和过期时间等(生命周期作用域);
把这个包装后的商品放入自动售货机后,它就变成了在售商品(组件)。
当有顾客需要某个商品时,他只要对着售货机报一个商品名(服务名),自动售货机找到对应商品,抛出给客户,这个抛给你的过程,就叫做注入你;
而且这个售货机比较智能,抛出前还可以先判断商品是不是过期了,该不该抛给你。
依赖注入的步骤
- 注册,创建映射关系
包含三种生命周期:AddTransient,每次注入或请求时都会创建转瞬即逝的服务;AddScoped,是按范围创建的,在Web应用程序中,每个Web请求都会创建一个新的独立服务范围;其中AddTransient和AddScoped每次注册的服务都会在被使用一次之后,被GC销毁掉。AddSingleton,每个DI容器创建一个单例服务,这通常意味着它们在每个应用程序只创建一次,然后用于整个应用程序生命周期,会一直存在内存里。
public void ConfigureServices(IServiceCollection services)
{
// 注册
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
- 创建,new实例化
public class ValuesController : ControllerBase
{
public IHttpContextAccessor HttpContextAccessor { get; set; }
// 创建,这个时候去new实例
public ValuesController(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
}
- 调用/使用
public ActionResult<IEnumerable<string>> Get()
{
//调用
var name = _accessor.HttpContext.User.Identity.Name;
}
aop(Aspect Oriented Programming),对关注切面关注点业务逻辑的解耦
可以参考:https://segmentfault.com/a/11...
基本概念
通过预编译方式和运行期动态代理实现程序功能的统一维护的一种思想。
思想的来源
从上到下:
- 面向过程编程。就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
- 面向对象编程 OOP。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合(对公共行为的一个封装)。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。例子:比如我们有一台演出,为简单起见,假设有如下流程:
——主持人开场function
——演员一表演function
——演员二表演function
——主持人总结function
用面向过程的思想来分析,就是先完成主持人开场,再完成演员一的表演,再完成演员二的表演,最后完成主持人的总结。
而如果用面向对象的思想来分析,就应该是这样的。这个演出由两大部分组成:主持人、演员。与主持人相关的:开场、总结。与演员相关的:演员编号、所演的节目等等。在调用对象的时候实例化即可。平时.net开发中不是百分之百利用面向对象编程思想,其实里面参杂着面向过程的思想。
- 面向抽象编程。抽象和接口虽好,但对所有不相干的对象建立共同的接口或父类未免有些生硬。
- GoF 四人 23种设计模式。但还是OOP的范围。
- 面向切面编程。例子:运送木材,它中间会因为关注的问题产生了一些其他的一些逻辑。比如说,日志记录、安全校验,还有异常处理,这些问题,本来不应该属于我们公共行为的类的一个动作,但是需要强行的加入到里面,意思就是,我们需要将分散的对象引入到公共行为,面向对象就无能为力了。这个时候就产生了面向切面编程思想,它是独立于面向对象编程的。切面编程就是在一个个封装好的对象之间,插入一个个分散的独立的对象。意思就是,这个行为我们没有写死在公共行为类里面,但是公共行为类可以直接调用。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。