工作一直都是写前端,而且都是偏业务的。相关的框架代码层面的了解还是有很大的缺失。一直想多写些维护性,可读性强的代码。

这段时间对控制反转ioc,这样的设计有了一个初步的了解。

前端为弱语言,平时代码的时候主要是过程化的思路去解决问题。虽然也会定义一些class,但是和面向对象还是存在很大的差别的。

平时写的偏业务,也不需要抽象,一般也就直接写个实现类,再这个基础上面再进行扩展。主要是不存在类型检测之类的,可以随意一些,相对的错误也不大好发现。

控制反转ioc主要是用于解耦方面,下面看下解耦的最基本的原则

  依赖倒置原则(Dependence Inversion Principle,DIP):
  A. 上层模块不应该依赖于下层模块,它们共同依赖于一个抽象。
  B. 抽象不能依赖于具象,具象依赖于抽象。

  简单点就是面向接口编程,具体实现具体类可以更换,只要是实现了某个接口就行。听起来像是鸭子类型

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

举个场景,比如当前页面结构

     Page {
Header
Main {
       SideBar
MainContent {
detail
comment
}
   }

简易代码如下

 class Detail {}
class Comment {
constructor() {
console.log('a');
}
}
class SideBar {}
class Header{}
class MainContent {
constructor() {
this.detail = new Detail();
this.comment = new Comment();
}
} class Main {
constructor() {
this.sideBar = new SideBar();
this.mainContent = new MainContent();
}
} class Page {
constructor() {
this.header = new Header();
this.main = new Main();
}
} new Page();

我们的评论变了,换成了递归回复console.log('b'),最直接的就是修改Comment类的实现,但是我们的Comment存在a,b,c,d模式,并且同时都可能存在的,看用户的选择的该怎么处理呢??

我们可能就需要考虑在Page上面增加一个commentModel的参数,一直往下面传下去,把Comment类改成一个工厂,根据commentModel的参数返回不同的实现

 class Detail {}
class SideBar {}
class Header{}
class CommentA {
constructor() {
console.log('a');
}
}
class CommentB {
constructor() {
console.log('a');
}
}
class CommentFactory {
constructor(commentModel) {
if (commentModel === 'a') {
return new CommentA();
} else if (commentModel === 'b') {
return new CommentB();
}
}
}
class MainContent {
constructor(commentModel) {
this.detail = new Detail();
this.comment = new CommentFactory(commentModel);
}
}
class Main {
constructor(commentModel) {
this.sideBar = new SideBar();
this.mainContent = new MainContent(commentModel);
}
}
class Page {
constructor(commentModel = 'a') {
this.commentModel = commentModel;
this.header = new Header();
this.main = new Main(commentModel);
}
}
new Page();

  上面可以看到为了解决上面的需求,我们差不多所有的相关的类都改了一遍,可我们只是想让comment灵活配置,就这么麻烦么???所以我们就需要依赖倒置,让代码结构解耦

 class Detail {}
class SideBar {}
class Header{}
class CommentA {
constructor() {
console.log('a');
}
}
class CommentB {
constructor() {
console.log('a');
}
}
class CommentFactory {
constructor(commentModel) {
if (commentModel === 'a') {
return new CommentA();
} else if (commentModel === 'b') {
return new CommentB();
}
}
}
class MainContent {
constructor(detail, comment) {
this.detail = detail;
this.comment = comment;
}
}
class Main {
constructor(sideBar, mainContent) {
this.sideBar = sideBar;
this.mainContent = mainContent;
}
}
class Page {
constructor(header, main) {
this.header = header;
this.main = main;
}
}
var mainContent = new MainContent(new Detail(), new CommentFactory('a'));
var main = new Main(new SideBar(), mainContent);
new Page(new Header, main);

依赖倒置后,我们只需要传入具体的实现类。对于类的定义,其实是依赖了接口,

现在项目中都是通过把page引用往内部传递,或者把page对象最为全局对象去使用,这样处理影响了代码的可维护性,公用性,耦合性

复杂项目中的依赖关系错综复杂,所以我们就需要引入控制反转控制器(Container)和依赖注入,让Container 管理 具体的对象,通过依赖注入,在代码层面绑定 变量和对象实例的关系,自动创建

下面就通过 typescript + reflect-metadata + inversify 来实现上面的代码

接口定义,大部分绑定都是接口,而不是具体的实现

//interfaces.ts 接口定义
export interface IComment {
}
export interface IDetail {
}
export interface ISideBar {
}
export interface IHeader {
}
export interface IComment {
}
export interface IMainContent {
detail: IDetail;
comment: IComment;
}
export interface IMain {
sideBar: ISideBar;
mainContent: IMainContent;
}
export interface IPage {
header: IHeader;
main: IMain;
}
//types.ts类型,其实可以理解为字符串
const TYPES = {
Detail: Symbol.for("Detail"),
SideBar: Symbol.for("SideBar"),
Header: Symbol.for("Header"),
Comment: Symbol.for("Comment"),
MainContent: Symbol.for("MainContent"),
Main: Symbol.for("Main"),
Page: Symbol.for("Page"),
}; export { TYPES };
//entities.ts 具体实现类
import { injectable, inject } from "inversify";
import "reflect-metadata";
import { IComment, IDetail, ISideBar, IHeader, IMainContent, IMain, IPage} from "./interfaces";
import { TYPES } from "./types"; @injectable()
class Detail implements IDetail{}
@injectable()
class SideBar implements ISideBar{}
@injectable()
class Header implements IHeader{}
@injectable()
class CommentA implements IComment{
constructor() {
console.log('a');
}
}
@injectable()
class CommentB implements IComment {
constructor() {
console.log('b');
}
}
@injectable()
class MainContent implements IMainContent {
@inject(TYPES.Detail) detail: IDetail; //依赖注入
@inject(TYPES.Comment) comment: IComment;
}
@injectable()
class Main implements IMain {
@inject(TYPES.SideBar) sideBar: ISideBar;
@inject(TYPES.MainContent) mainContent: IMainContent;
}
@injectable()
class Page implements IPage {
@inject(TYPES.Header) header: IHeader;
@inject(TYPES.Main) main: IMain;
}
export { CommentA, CommentB, Detail, SideBar, Header, MainContent, Main, Page };
//inversify.config.ts     控制器定义,接口绑定对应的实现类
import { Container } from "inversify";
import { TYPES } from "./types";
import { IComment, IDetail, ISideBar, IHeader, IMainContent, IMain, IPage } from "./interfaces";
import { CommentA, CommentB, Detail, SideBar, Header, MainContent, Main, Page } from "./entities"; const containerA = new Container();
containerA.bind<IComment>(TYPES.Comment).to(CommentA);
containerA.bind<IDetail>(TYPES.Detail).to(Detail);
containerA.bind<ISideBar>(TYPES.SideBar).to(SideBar);
containerA.bind<IHeader>(TYPES.Header).to(Header);
containerA.bind<IMainContent>(TYPES.MainContent).to(MainContent);
containerA.bind<IMain>(TYPES.Main).to(Main);
containerA.bind<IPage>(TYPES.Page).to(Page); const containerB = new Container();
containerB.bind<IComment>(TYPES.Comment).to(CommentB);
containerB.bind<IDetail>(TYPES.Detail).to(Detail);
containerB.bind<ISideBar>(TYPES.SideBar).to(SideBar);
containerB.bind<IHeader>(TYPES.Header).to(Header);
containerB.bind<IMainContent>(TYPES.MainContent).to(MainContent);
containerB.bind<IMain>(TYPES.Main).to(Main);
containerB.bind<IPage>(TYPES.Page).to(Page); export { containerA, containerB };
//index.ts
import { containerA, containerB } from "./inversify.config";
import { TYPES } from "./types";
import { IPage } from "./interfaces"; containerA.get<IPage>(TYPES.Page);
containerB.get<IPage>(TYPES.Page);

当然我们也可以通过注入工厂方法的方式去修改下。

控制反转其实还有一个场景就是测试,A以来B,只是B的值简单处理,但是要构造一个具体的B对象很麻烦。我们可以通过ioc,把B对象替换成一个简单对象来对A进行详细测试。

最新文章

  1. Windows多网卡上网设置
  2. 移动端调试工具推荐 小苹果和debugap
  3. Python学习教程(learning Python)--3.2 if-else分支语句
  4. 上海洋码头(www.ymatou.com)急招技术人才(职位:互联网软件开发工程师,.NET网站架构师,Web前端开发工程师,高级测试工程师,产品经理)
  5. -join 和 -split 用法
  6. Failed dependencies: 检查依赖性错误 解决方法
  7. SAE Python使用经验 好文推荐
  8. XSS学习笔记(四)-漏洞利用全过程
  9. js 上传图片预览
  10. [转]intent 传递对象
  11. MySql学习(七) —— 查询性能优化 深入理解MySql如何执行查询
  12. 团队作业4---第一次项目冲刺(ALpha)版本 第五天
  13. svn + nginx unit + python3自动化发布web服务方法
  14. MongoDB-BSON
  15. HDOJ 1754 I Hate It (线段树)
  16. 观实验室PPT演讲有感
  17. Java操作Kafka
  18. ireport表单制作
  19. Ubuntu去掉命令行前用户名和主机名方法
  20. Eclipse中Editor开启Auto-completion

热门文章

  1. ubuntu下编译和使用libxml2
  2. mouse without borders无界鼠标使用教程
  3. git clone时加上--depth 1
  4. WIN 10 看不到SAMBA共享的硬盘
  5. mysql常用命令、非交互式mysql命令看29条
  6. 生成count位随机数工具类
  7. 【C# 开发技巧】 C#中WinForm程序退出方法技巧总结
  8. Fakes中Shim的2种方法
  9. darknet标签转化为COCO标签
  10. amazeUI modal 模态框 关闭属性