DesignPatterns_TypeScript | * 前言: ·背景: --
kandi X-RAY | DesignPatterns_TypeScript Summary
kandi X-RAY | DesignPatterns_TypeScript Summary
DesignPatterns_TypeScript is a TypeScript library. DesignPatterns_TypeScript has no bugs, it has no vulnerabilities, it has a Permissive License and it has low support. You can download it from GitHub.
*前言: ·背景: -- 经典23种设计模式来源于静态语言,基于js的设计模式书籍往往又只是单单通过动态语言js来实现,不利于真正理解设计模式 -- 因为设计模式很难验证对错,网络上存在大量错误的设计模式教程误导新人 -- 设计模式资料普遍只讲述模式的实现步骤,缺乏对理解这些步骤的前置知识进行讲解,导致对设计模式的学习只是表面招式 ·项目内容: -- 了解设计模式的来龙去脉,补充学习设计模式需要了解的前置知识,如:设计原则、解耦、控制反转、依赖注入、反射、注解、装饰器等内容 -- 重点实现typescript版设计模式代码,补充实现java/javascript版设计模式代码. *什么是设计模式? ·狭义的设计模式:指的是gof四人组在《design patterns: elements of reusable object-oriented software》一书中提出的23种设计模式。 ·广义的设计模式:最早的设计模式是美国著名建筑大师克里斯托夫·亚历山大在他的著作《建筑模式语言:城镇、建筑、构造》中描述了一些常见的建筑设计问题,并提出了253种关于对城镇、邻里、住宅、花园和房间等进行设计的基本模式。后来软件界也开始论坛设计模式的话题,因为这也是类似的。所以设计模式指的是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,是可以反复使用的。. *为什么要学习设计模式? ·什么是高质量代码: 在弄清楚为什么要学习设计模式之前,我们先思考什么才是高质量的代码?高质量代码的特点: -- 代码bug少 -- 代码够健壮 -- 代码易测试 -- 代码易阅读 -- 代码易扩展 -- 代码性能高 更多高质量代码的内容可查看我的这篇总结:·如何开发出高质量代码呢?一个重要的方式就是遵守前人总结的7大设计原则: -- 开闭原则:要对扩展开放,对修改关闭 -- 里氏替换原则:不要破坏继承体系 -- 依赖倒置原则:要面向接口编程 -- 单一职责原则:实现类要职责单一 -- 接口隔离原则:在设计接口的时候要精简单一 -- 迪米特法则:要降低耦合度 -- 合成复用原则:要优先使用组合或者聚合关系复用,少用继承关系复用 ·设计原则与设计模式的关系: -- 好的程序需要靠7大设计原则完成,但是由于语言的缺陷导致程序需要按照一定复杂度的步骤去实现这些设计原则,而这些步骤通常都是固定的,就像武功中的套路招式一样,如果再给这些套路加上好听的名字,这就成了设计模式。也就是说23种设计模式就是7大设计原则在某些语言的具体实现的一种方式,每个设计模式的背后我们都能找到其依靠的一种或多种设计原则。换句话说就是,只要我们写代码遵循设计原则,代码就会自然而然变成了某些设计模式,这也在王垠的《解密“设计模式”》中得到证明。 -- 由于现实问题的复杂性,往往导致代码不可能同时满足所有的设计原则,甚至要违背部分设计原则,这里就会有一个最佳实践的问题了。而设计模式就为解决特定问题提供了最佳实践方案,以至于学习了设计模式后,在遇到特定问题时,我们脑子很容易知道如何在满足设计原则最优解的基础上实现代码的编写。 -- 虽然设计原则为开发出高质量代码指明了方向,但没有对程序的高性能等做出指示,而设计模式在这方面做了补充。 ·结论: -- 设计模式通过写代码的形式帮助我们更好的学习理解设计原则,以实现高质量代码; -- 为实现设计原则的招式统一命名; -- 为特定场景问题的提供最优解决方案; -- 补充了设计原则在构建高性能程序等方面的内容。. *解耦与高质量代码之间的关系: ·解耦(低耦合)在某个角度上说属于高质量代码的一个重要体现,但不是全部。高质量代码具体还体现在代码可测试性、代码健壮性、代码高性能等等许多细节方面。 ·耦合性高低的衡量标准:-- 依赖性 -- 正交性 -- 紧凑性 ·实际上这3个指标大概分别对应着7大设计原则中的迪米特法则、单一职责原则、接口隔离原则等原则,而7大设计原则也就是高质量软件的编写原则,所以也印证了解耦是属于高质量代码的一部分体现。. *学习设计模式的常见问题: ·学习设计模式的核心是掌握设计模式的意图是什么:不同的设计模式在不同的语言中会有不同表现形式,千万不要被模式中的各种概念迷惑,而只学到表面套路。我看到很多热门教程是示例代码甚至是错误的,例如这个超过2k star的设计模式项目,他的工厂方法的实现就不对,如果要增加新的产品,他就必须必须修改createproduct代码,但这违背了开闭原则。导致这个错误的原因就是因为作者没有理解工厂方法的目的:对简单工厂模式的进一步抽象化,使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。又如这个项目的原型模式的实现也是错误的,原型模式的场景是因为复制一个对象比new一个对象更高效。所以学习设计模式的正确姿势应该是:掌握该设计模式的意图,并在遵守设计原则的情况下去实现它。 ·设计模式的招式并不是一成不变的,它在不同语言中会有不同的表现,但背后的思想和设计原则都是一样的,例如装饰器模式在java中需要定义抽象构件、抽象装饰,而ts只需一个@即可搞定。例如java8加入lambda后,很多设计模式的实现都会改变。厉害的语言不需要那么多设计模式也能满足设计原则,参考:·设计模式在不同语言之间的实现原理:gof的《设计模式》一书是针对面向对象语言提炼的技巧,但并不意味着设计模式只能用面向对象语言来写,实际上动态语言也是可以使用设计模式的。例如java这种静态编译型语言中,无法动态给已存在的对象添加职责,所有一般通过包装类的方式来实现装饰者模式。但是js这种动态解释型语言中,给对象动态添加职责是再简单不过的事情。这就造成了js的装饰者模式不再关注给对象动态添加职责,而是关注于给函数动态添加职责。例如有人模拟js版本的工厂模式,而生硬地把创建对象延迟到子类中。实际上,在java等静态语言中,让子类来“决定”创建何种对象的原因是为了让程序迎合依赖倒置原则。在这些语言中创建对象时,先解开对象类型之间的耦合关系非常重要,这样才有机会在将来让对象表现出多态性。而在js这类类型模糊的语言中,对象多态性是天生的,一个变量既可以指向一个类,又可以随时指向另一个类。js不存在类型耦合的问题,自然也没有刻意去把对象“延迟”到子类创建,也就是说,js实际上是不需要工厂方法模式的。模式的存在首先是为了满足设计原则的。 ·分辨模式的关键是意图而不是结构:在设计模式的学习中,有人经常会发出这样的疑问:代理模式和装饰着模式,策略模式和状态模式,这些模式的类图看起来几乎一模一样,他们到底有什么区别?实际上这种情况是普遍存在的,许多模式的类图看起来都差不多,模式只有放在具体的环境下才有意义。比如我们的手机,用它当电话的时候它就是电话;用它当闹钟的时候它就是闹钟;用它玩游戏的时候他就是游戏机。有很多模式的类图和结构确实很相似,但这不太重要,辨别模式的关键是这个模式出现的场景,以及为我们解决了什么问题。 ·设计模式一直在发展,例如现在逐渐流行起来的模块模式、沙箱模式等,但真正得到人们的认可还需要时间的检验。 ·设计模式的合理使用: -- 总体来说,使用设计模式的必要性的程度是逐级递增的:应用程序(application) < 工具包/类库(toolkit/library) < 框架(framework) -- 具体来说,我们不必刻意为了设计模式而设计模式,例如我们当前只需要创建一个产品,而未来没有多大可能增加新产品时,我们就用简单工厂模式,而无需为了符合开闭原则而去选择工厂方法模式。引用轮子哥的话:“为了合理的利用设计模式,我们应该明白一个概念,叫做扩展点。扩展点不是天生就有的,而是设计出来的。我们设计一个软件的架构的时候,我们也要同时设计一下哪些地方以后可以改,哪些地方以后不能改。倘若你的设计不能满足现实世界的需要,那你就要重构,把有用的扩展点加进去,把没用的扩展点去除掉。这跟你用不用设计模式没关系,跟你对具体的行业的理解有关系。倘若你设计好了每一个扩展点的位置,那你就可以在每一个扩展点上应用设计模式,你就不需要去想到底这个扩展点要怎么实现他才会真正成为一个扩展点,你只需要按照套路写出来就好了。 ·设计模式命名:为了他人能快速理解你使用的设计模式,建议参考模式英文名进行命名,例如:建造者模式——xxxbuilder,单例模式——xxxsingleton,适配器模式——xxxadapter,状态模式——xxxstate,策略模式——xxxstratege等等。 ·对设计模式的误解: -- 习惯把静态语言的设计模式照搬到动态语言中 -- 习惯根据模式名称去臆测该模式的一切. *开闭原则: ·定义:一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。 ·解释: 当软件系统需要面对新的需求时,我们应该尽量保证系统的设计框架是稳定的。如果一个软件设计符合开闭原则,那么可以非常方便地对系统进行扩展,而且在扩展时无须修改现有代码,使得软件系统在拥有适应性和灵活性的同时具备较好的稳定性和延续性。 为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。在java、c#等编程语言中,可以为系统定义一个相对稳定的抽象层,而将不同的实现行为移至具体的实现层中完成。在很多面向对象编程语言中都提供了接口、抽象类等机制,可以通过它们定义系统的抽象层,再通过具体类来进行扩展。如果需要修改系统的行为,无须对抽象层进行任何改动,只需要增加新的具体类来实现新的业务功能即可,实现在不修改已有代码的基础上扩展系统的功能,达到开闭原则的要求。 在软件开发中,一般不把对配置文件的修改认为是对系统源代码的修改。如果一个系统在扩展时只涉及到修改配置文件,而原有的代码没有做任何修改,该系统即可认为是一个符合开闭原则的系统。所以,配置文件+反射技术在java、c#、.net等各类后端语言中大量采用,以实现完全开闭原则。 ·开闭原则的实现由里氏替换原则和依赖倒置原则来完成。. *里氏替换原则: ·背景: 继承的优点:代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性、提高代码的重用性、子类可以形似父类,但又异于父类、提高代码的可扩展性、提高产品或项目的开放性。 继承的缺点:继承是侵入性的:只要继承,就必须拥有父类的所有属性和方法、降低代码的灵活性:子类必须拥有父类的属性和方法、增强了耦合性:当父类的常量、变量和方法被修改时,必需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果大片的代码需要重构 里氏替换原则能够克服继承的缺点。 ·定义:子类可以扩展父类的功能,但不能改变父类原有的功能。父类能出现的地方都可以用子类来代替,而且换成子类也不会出现任何错误或异常,而使用者也无需知道是父类还是子类,但反过来则不成立。 ·解释: lsp的原定义比较复杂,我们一般对里氏替换原则 lsp的解释为:子类对象能够替换父类对象,而程序逻辑不变。 里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。例如:我喜欢动物,那我一定喜欢狗,因为狗是动物的子类;但是我喜欢狗,不能据此断定我喜欢动物,因为我并不喜欢老鼠,虽然它也是动物。 里氏替换原则有至少以下两种含义: ①里氏替换原则是针对继承而言的,如果继承是为了实现代码重用,也就是为了共享方法,那么共享的父类方法就应该保持不变,不能被子类重新定义。子类只能通过新添加方法来扩展功能,父类和子类都可以实例化,而子类继承的方法和父类是一样的,父类调用方法的地方,子类也可以调用同一个继承得来的,逻辑和父类一致的方法,这时用子类对象将父类对象替换掉时,当然逻辑一致,相安无事。 ②如果继承的目的是为了多态,而多态的前提就是子类覆盖并重新定义父类的方法,为了符合lsp,我们应该将父类定义为抽象类,并定义抽象方法,让子类重新定义这些方法,当父类是抽象类时,父类就是不能实例化,所以也不存在可实例化的父类对象在程序里。也就不存在子类替换父类实例(根本不存在父类实例了)时逻辑不一致的可能。 不符合lsp的最常见的情况是,父类和子类都是可实例化的非抽象类,且父类的方法被子类重新定义,这一类的实现继承会造成父类和子类间的强耦合,也就是实际上并不相关的属性和方法牵强附会在一起,不利于程序扩展和维护。 如何符合lsp?总结一句话 —— 就是尽量不要从可实例化的父类中继承,而是要使用基于抽象类和接口的继承。 ·最佳实践: ①子类必须完全实现父类的抽象方法,但不能覆盖父类的非抽象方法; ②子类中可以增加自己特有的方法; ③当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数要更宽松; ④当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。 ·里氏代换原则和依赖倒置原则一样,是开闭原则的具体实现手段之一。. *依赖倒置原则: ·定义: 高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。 ·解释: 高层模块不应该直接依赖于底层模块的具体实现,而应该依赖于底层的抽象。换言之,模块间的依赖是通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。 接口和抽象类不应该依赖于实现类,而实现类依赖接口或抽象类。这一点其实不用多说,很好理解,“面向接口编程”思想正是这点的最好体现。 ·解释: 常规我们认为上层模块应该依赖下层,但是这也有个问题就是,下层变动将导致“牵一发动全身”。依赖倒置就是反常规思维,将原本的高层建筑依赖底层建筑“倒置”过来,变成底层建筑依赖高层建筑。高层建筑决定需要什么,底层去实现这样的需求,但是高层并不用管底层是怎么实现的。这样就不会出现前面的“牵一发动全身”的情况。当然,严格的来说上层不应该依赖下层,而依赖自身接口,通过注入的方式依赖其他接口。 ·控制反转(inversion of control): 就是依赖倒置原则的一种代码设计的思路。具体采用的实现方法就是所谓的依赖注入(dependency injection)。 ·控制反转容器(ioc container): 因为采用了依赖注入,在初始化的过程中就不可避免的会写大量的new,并且还要要管理各个对象之间依赖关系,所以这里使用工厂方法还是比较麻烦。而ioc容器就解决以上2个问题。这个容器可以自动对你的代码进行初始化,你只需要维护一个configuration(可以是xml可以是一段代码),而不用每次初始化一实例都要亲手去写那一大段初始化的代码。另外一个好处是:我们在创建实例的时候不需要了解其中的依赖细节。 ·java的反射机制: -- 实现依赖倒置原则的基础是代码的抽象化。java提供了抽象类、接口、泛型等方式进行抽象,但这些还是不够抽象,最抽象的状态是像js一样的鸭子类型。但是由于java在编译阶段存在类型检查系统,要避开类型检测,也就必须要绕过编译阶段。而反射的操作都是中编译成字节码以后的操作,这个阶段不存在类型检查,所以可以利用反射编写各种抽象代码。因为抽象具有更强兼容能力,所以各种框架会经常使用到反射技术。 -- typescirpt的类型检查也只发生在编译阶段 -- 类也是对象,类是java.lang.class类的实例对象 -- java类的静态加载和动态加载: -- 静态加载就是编译时加载,此时将进行类型检查,变量需满足理论的数据类型 -- 动态加载就是运行时加载,发生在编译后,此时才真正能确定对象的确切类型 -- 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类。new创建对象是静态加载类,在编译时刻就要加载所有可能用到的类,这样导致某一个类(的类型)出现问题,其他所有类都将无法使用(无法通过编译)。通过反射可以实现动态加载类解决这个问题。动态加载的方法,即通过类的类类型创建该类的对象 -- java反射机制允许程序在运行时透过reflection apis取得任意一个已知名称的class的内部信息,包括modifiers(如public、static等)、superclass(如object)、实现的interfaces(如serializable)、fields(属性)和methods(方法)(但不包括methods定义),可于运行时改变fields的内容,也可调用methods -- 反射api: -- 获取类:class c=class.forname("alunbar");c.newinstance(); -- 获取类的方法信息,代码示例:class c=obj.getclass();method[] ms=c.getmethods();//参数列表获取省略 -- 获取类的成员变量信息,代码示例:class c=obj.getclass();field[] fs=c.getfields(); -- 获取类的构造方法信息信息,代码示例:class c=obj.getclass();constructor[] cs=c.getconstructors();//参数列表获取省略 -- 方法的反射调用,操作步骤: 1、根据方法的名称和参数列表获取唯一的方法; 2、拿到方法后,method.invoke(对象,参数列表)。 -- 配置文件 + 反射机制 实现开闭原则:在引入配置文件和反射机制后,需要更换或增加新的具体类将变得很简单,只需增加新的具体类并修改配置文件即可,无须对现有类库和客户端代码进行任何修改,完全符合开闭原则。在很多设计模式中都可以通过引入配置文件和反射机制来对客户端代码进行改进,如在抽象工厂模式中可以将具体工厂类类名存储在配置文件中,在适配器模式中可以将适配器类类名存储在配置文件中,在策略模式中可以将具体策略类类名存储在配置文件中等等。通过对代码的改进,可以让系统具有更好的扩展性和灵活性,更加满足各种面向对象设计原则的要求。 -- 小结:代码是现实世界的抽象,由于世界复杂的本质,注定不会有银弹。而java的反射机制为java提供一种编写足够抽象代码的手段 ·注解: 在编码过程中,为了更好的理解程序,我们通常会加上注释代码,这样可以方便程序员理解。而注解就是注释的一种,但是与一般的注释不同的是,注解需要按一定规则编写(见自定义注解),并且注解可以被程序解析,并根据解析结果进行相应处理。如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了。使用注解的过程中,很重要的一部分就是创建于使用注解处理器。java se5扩展了反射机制的api,以帮助程序员快速的构造自定义注解处理器。 ·元数据:指用来描述数据的数据。 ·元数据和注解:注解可以用来描述数据,所有注解是元数据的一种实现方式。 ·注解的作用:通过解析注解可以拿到注解中的属性和方法,并实现各式功能。 例如可以搭配反射实现开闭原则,因为注解可以被反射解析出来,此时的注解相当于一个配置文件。 ·在java中,除了注解充当配置文件,还可以用xml作为配置文件,但注解优点明显: 1、在class文件中,可以降低维护成本,annotation的配置机制很明显简单; 2、不需要第三方的解析工具,利用java反射技术就可以完成任务; 3、编辑期可以验证正确性,查错变得容易; 4、提高开发效率 ·ts中的注解:ts中其实没有注解的概念,但是前端界曾经还是有语言借鉴了注解:angular2的atscript语言,它能完完全全的单纯附加元数据。例如: @component({ selector: 'app' }) class appcomponent {} 等价于 class appcomponent {}
*前言: ·背景: -- 经典23种设计模式来源于静态语言,基于js的设计模式书籍往往又只是单单通过动态语言js来实现,不利于真正理解设计模式 -- 因为设计模式很难验证对错,网络上存在大量错误的设计模式教程误导新人 -- 设计模式资料普遍只讲述模式的实现步骤,缺乏对理解这些步骤的前置知识进行讲解,导致对设计模式的学习只是表面招式 ·项目内容: -- 了解设计模式的来龙去脉,补充学习设计模式需要了解的前置知识,如:设计原则、解耦、控制反转、依赖注入、反射、注解、装饰器等内容 -- 重点实现typescript版设计模式代码,补充实现java/javascript版设计模式代码. *什么是设计模式? ·狭义的设计模式:指的是gof四人组在《design patterns: elements of reusable object-oriented software》一书中提出的23种设计模式。 ·广义的设计模式:最早的设计模式是美国著名建筑大师克里斯托夫·亚历山大在他的著作《建筑模式语言:城镇、建筑、构造》中描述了一些常见的建筑设计问题,并提出了253种关于对城镇、邻里、住宅、花园和房间等进行设计的基本模式。后来软件界也开始论坛设计模式的话题,因为这也是类似的。所以设计模式指的是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,是可以反复使用的。. *为什么要学习设计模式? ·什么是高质量代码: 在弄清楚为什么要学习设计模式之前,我们先思考什么才是高质量的代码?高质量代码的特点: -- 代码bug少 -- 代码够健壮 -- 代码易测试 -- 代码易阅读 -- 代码易扩展 -- 代码性能高 更多高质量代码的内容可查看我的这篇总结:·如何开发出高质量代码呢?一个重要的方式就是遵守前人总结的7大设计原则: -- 开闭原则:要对扩展开放,对修改关闭 -- 里氏替换原则:不要破坏继承体系 -- 依赖倒置原则:要面向接口编程 -- 单一职责原则:实现类要职责单一 -- 接口隔离原则:在设计接口的时候要精简单一 -- 迪米特法则:要降低耦合度 -- 合成复用原则:要优先使用组合或者聚合关系复用,少用继承关系复用 ·设计原则与设计模式的关系: -- 好的程序需要靠7大设计原则完成,但是由于语言的缺陷导致程序需要按照一定复杂度的步骤去实现这些设计原则,而这些步骤通常都是固定的,就像武功中的套路招式一样,如果再给这些套路加上好听的名字,这就成了设计模式。也就是说23种设计模式就是7大设计原则在某些语言的具体实现的一种方式,每个设计模式的背后我们都能找到其依靠的一种或多种设计原则。换句话说就是,只要我们写代码遵循设计原则,代码就会自然而然变成了某些设计模式,这也在王垠的《解密“设计模式”》中得到证明。 -- 由于现实问题的复杂性,往往导致代码不可能同时满足所有的设计原则,甚至要违背部分设计原则,这里就会有一个最佳实践的问题了。而设计模式就为解决特定问题提供了最佳实践方案,以至于学习了设计模式后,在遇到特定问题时,我们脑子很容易知道如何在满足设计原则最优解的基础上实现代码的编写。 -- 虽然设计原则为开发出高质量代码指明了方向,但没有对程序的高性能等做出指示,而设计模式在这方面做了补充。 ·结论: -- 设计模式通过写代码的形式帮助我们更好的学习理解设计原则,以实现高质量代码; -- 为实现设计原则的招式统一命名; -- 为特定场景问题的提供最优解决方案; -- 补充了设计原则在构建高性能程序等方面的内容。. *解耦与高质量代码之间的关系: ·解耦(低耦合)在某个角度上说属于高质量代码的一个重要体现,但不是全部。高质量代码具体还体现在代码可测试性、代码健壮性、代码高性能等等许多细节方面。 ·耦合性高低的衡量标准:-- 依赖性 -- 正交性 -- 紧凑性 ·实际上这3个指标大概分别对应着7大设计原则中的迪米特法则、单一职责原则、接口隔离原则等原则,而7大设计原则也就是高质量软件的编写原则,所以也印证了解耦是属于高质量代码的一部分体现。. *学习设计模式的常见问题: ·学习设计模式的核心是掌握设计模式的意图是什么:不同的设计模式在不同的语言中会有不同表现形式,千万不要被模式中的各种概念迷惑,而只学到表面套路。我看到很多热门教程是示例代码甚至是错误的,例如这个超过2k star的设计模式项目,他的工厂方法的实现就不对,如果要增加新的产品,他就必须必须修改createproduct代码,但这违背了开闭原则。导致这个错误的原因就是因为作者没有理解工厂方法的目的:对简单工厂模式的进一步抽象化,使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。又如这个项目的原型模式的实现也是错误的,原型模式的场景是因为复制一个对象比new一个对象更高效。所以学习设计模式的正确姿势应该是:掌握该设计模式的意图,并在遵守设计原则的情况下去实现它。 ·设计模式的招式并不是一成不变的,它在不同语言中会有不同的表现,但背后的思想和设计原则都是一样的,例如装饰器模式在java中需要定义抽象构件、抽象装饰,而ts只需一个@即可搞定。例如java8加入lambda后,很多设计模式的实现都会改变。厉害的语言不需要那么多设计模式也能满足设计原则,参考:·设计模式在不同语言之间的实现原理:gof的《设计模式》一书是针对面向对象语言提炼的技巧,但并不意味着设计模式只能用面向对象语言来写,实际上动态语言也是可以使用设计模式的。例如java这种静态编译型语言中,无法动态给已存在的对象添加职责,所有一般通过包装类的方式来实现装饰者模式。但是js这种动态解释型语言中,给对象动态添加职责是再简单不过的事情。这就造成了js的装饰者模式不再关注给对象动态添加职责,而是关注于给函数动态添加职责。例如有人模拟js版本的工厂模式,而生硬地把创建对象延迟到子类中。实际上,在java等静态语言中,让子类来“决定”创建何种对象的原因是为了让程序迎合依赖倒置原则。在这些语言中创建对象时,先解开对象类型之间的耦合关系非常重要,这样才有机会在将来让对象表现出多态性。而在js这类类型模糊的语言中,对象多态性是天生的,一个变量既可以指向一个类,又可以随时指向另一个类。js不存在类型耦合的问题,自然也没有刻意去把对象“延迟”到子类创建,也就是说,js实际上是不需要工厂方法模式的。模式的存在首先是为了满足设计原则的。 ·分辨模式的关键是意图而不是结构:在设计模式的学习中,有人经常会发出这样的疑问:代理模式和装饰着模式,策略模式和状态模式,这些模式的类图看起来几乎一模一样,他们到底有什么区别?实际上这种情况是普遍存在的,许多模式的类图看起来都差不多,模式只有放在具体的环境下才有意义。比如我们的手机,用它当电话的时候它就是电话;用它当闹钟的时候它就是闹钟;用它玩游戏的时候他就是游戏机。有很多模式的类图和结构确实很相似,但这不太重要,辨别模式的关键是这个模式出现的场景,以及为我们解决了什么问题。 ·设计模式一直在发展,例如现在逐渐流行起来的模块模式、沙箱模式等,但真正得到人们的认可还需要时间的检验。 ·设计模式的合理使用: -- 总体来说,使用设计模式的必要性的程度是逐级递增的:应用程序(application) < 工具包/类库(toolkit/library) < 框架(framework) -- 具体来说,我们不必刻意为了设计模式而设计模式,例如我们当前只需要创建一个产品,而未来没有多大可能增加新产品时,我们就用简单工厂模式,而无需为了符合开闭原则而去选择工厂方法模式。引用轮子哥的话:“为了合理的利用设计模式,我们应该明白一个概念,叫做扩展点。扩展点不是天生就有的,而是设计出来的。我们设计一个软件的架构的时候,我们也要同时设计一下哪些地方以后可以改,哪些地方以后不能改。倘若你的设计不能满足现实世界的需要,那你就要重构,把有用的扩展点加进去,把没用的扩展点去除掉。这跟你用不用设计模式没关系,跟你对具体的行业的理解有关系。倘若你设计好了每一个扩展点的位置,那你就可以在每一个扩展点上应用设计模式,你就不需要去想到底这个扩展点要怎么实现他才会真正成为一个扩展点,你只需要按照套路写出来就好了。 ·设计模式命名:为了他人能快速理解你使用的设计模式,建议参考模式英文名进行命名,例如:建造者模式——xxxbuilder,单例模式——xxxsingleton,适配器模式——xxxadapter,状态模式——xxxstate,策略模式——xxxstratege等等。 ·对设计模式的误解: -- 习惯把静态语言的设计模式照搬到动态语言中 -- 习惯根据模式名称去臆测该模式的一切. *开闭原则: ·定义:一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。 ·解释: 当软件系统需要面对新的需求时,我们应该尽量保证系统的设计框架是稳定的。如果一个软件设计符合开闭原则,那么可以非常方便地对系统进行扩展,而且在扩展时无须修改现有代码,使得软件系统在拥有适应性和灵活性的同时具备较好的稳定性和延续性。 为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。在java、c#等编程语言中,可以为系统定义一个相对稳定的抽象层,而将不同的实现行为移至具体的实现层中完成。在很多面向对象编程语言中都提供了接口、抽象类等机制,可以通过它们定义系统的抽象层,再通过具体类来进行扩展。如果需要修改系统的行为,无须对抽象层进行任何改动,只需要增加新的具体类来实现新的业务功能即可,实现在不修改已有代码的基础上扩展系统的功能,达到开闭原则的要求。 在软件开发中,一般不把对配置文件的修改认为是对系统源代码的修改。如果一个系统在扩展时只涉及到修改配置文件,而原有的代码没有做任何修改,该系统即可认为是一个符合开闭原则的系统。所以,配置文件+反射技术在java、c#、.net等各类后端语言中大量采用,以实现完全开闭原则。 ·开闭原则的实现由里氏替换原则和依赖倒置原则来完成。. *里氏替换原则: ·背景: 继承的优点:代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性、提高代码的重用性、子类可以形似父类,但又异于父类、提高代码的可扩展性、提高产品或项目的开放性。 继承的缺点:继承是侵入性的:只要继承,就必须拥有父类的所有属性和方法、降低代码的灵活性:子类必须拥有父类的属性和方法、增强了耦合性:当父类的常量、变量和方法被修改时,必需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果大片的代码需要重构 里氏替换原则能够克服继承的缺点。 ·定义:子类可以扩展父类的功能,但不能改变父类原有的功能。父类能出现的地方都可以用子类来代替,而且换成子类也不会出现任何错误或异常,而使用者也无需知道是父类还是子类,但反过来则不成立。 ·解释: lsp的原定义比较复杂,我们一般对里氏替换原则 lsp的解释为:子类对象能够替换父类对象,而程序逻辑不变。 里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。例如:我喜欢动物,那我一定喜欢狗,因为狗是动物的子类;但是我喜欢狗,不能据此断定我喜欢动物,因为我并不喜欢老鼠,虽然它也是动物。 里氏替换原则有至少以下两种含义: ①里氏替换原则是针对继承而言的,如果继承是为了实现代码重用,也就是为了共享方法,那么共享的父类方法就应该保持不变,不能被子类重新定义。子类只能通过新添加方法来扩展功能,父类和子类都可以实例化,而子类继承的方法和父类是一样的,父类调用方法的地方,子类也可以调用同一个继承得来的,逻辑和父类一致的方法,这时用子类对象将父类对象替换掉时,当然逻辑一致,相安无事。 ②如果继承的目的是为了多态,而多态的前提就是子类覆盖并重新定义父类的方法,为了符合lsp,我们应该将父类定义为抽象类,并定义抽象方法,让子类重新定义这些方法,当父类是抽象类时,父类就是不能实例化,所以也不存在可实例化的父类对象在程序里。也就不存在子类替换父类实例(根本不存在父类实例了)时逻辑不一致的可能。 不符合lsp的最常见的情况是,父类和子类都是可实例化的非抽象类,且父类的方法被子类重新定义,这一类的实现继承会造成父类和子类间的强耦合,也就是实际上并不相关的属性和方法牵强附会在一起,不利于程序扩展和维护。 如何符合lsp?总结一句话 —— 就是尽量不要从可实例化的父类中继承,而是要使用基于抽象类和接口的继承。 ·最佳实践: ①子类必须完全实现父类的抽象方法,但不能覆盖父类的非抽象方法; ②子类中可以增加自己特有的方法; ③当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数要更宽松; ④当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。 ·里氏代换原则和依赖倒置原则一样,是开闭原则的具体实现手段之一。. *依赖倒置原则: ·定义: 高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。 ·解释: 高层模块不应该直接依赖于底层模块的具体实现,而应该依赖于底层的抽象。换言之,模块间的依赖是通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。 接口和抽象类不应该依赖于实现类,而实现类依赖接口或抽象类。这一点其实不用多说,很好理解,“面向接口编程”思想正是这点的最好体现。 ·解释: 常规我们认为上层模块应该依赖下层,但是这也有个问题就是,下层变动将导致“牵一发动全身”。依赖倒置就是反常规思维,将原本的高层建筑依赖底层建筑“倒置”过来,变成底层建筑依赖高层建筑。高层建筑决定需要什么,底层去实现这样的需求,但是高层并不用管底层是怎么实现的。这样就不会出现前面的“牵一发动全身”的情况。当然,严格的来说上层不应该依赖下层,而依赖自身接口,通过注入的方式依赖其他接口。 ·控制反转(inversion of control): 就是依赖倒置原则的一种代码设计的思路。具体采用的实现方法就是所谓的依赖注入(dependency injection)。 ·控制反转容器(ioc container): 因为采用了依赖注入,在初始化的过程中就不可避免的会写大量的new,并且还要要管理各个对象之间依赖关系,所以这里使用工厂方法还是比较麻烦。而ioc容器就解决以上2个问题。这个容器可以自动对你的代码进行初始化,你只需要维护一个configuration(可以是xml可以是一段代码),而不用每次初始化一实例都要亲手去写那一大段初始化的代码。另外一个好处是:我们在创建实例的时候不需要了解其中的依赖细节。 ·java的反射机制: -- 实现依赖倒置原则的基础是代码的抽象化。java提供了抽象类、接口、泛型等方式进行抽象,但这些还是不够抽象,最抽象的状态是像js一样的鸭子类型。但是由于java在编译阶段存在类型检查系统,要避开类型检测,也就必须要绕过编译阶段。而反射的操作都是中编译成字节码以后的操作,这个阶段不存在类型检查,所以可以利用反射编写各种抽象代码。因为抽象具有更强兼容能力,所以各种框架会经常使用到反射技术。 -- typescirpt的类型检查也只发生在编译阶段 -- 类也是对象,类是java.lang.class类的实例对象 -- java类的静态加载和动态加载: -- 静态加载就是编译时加载,此时将进行类型检查,变量需满足理论的数据类型 -- 动态加载就是运行时加载,发生在编译后,此时才真正能确定对象的确切类型 -- 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类。new创建对象是静态加载类,在编译时刻就要加载所有可能用到的类,这样导致某一个类(的类型)出现问题,其他所有类都将无法使用(无法通过编译)。通过反射可以实现动态加载类解决这个问题。动态加载的方法,即通过类的类类型创建该类的对象 -- java反射机制允许程序在运行时透过reflection apis取得任意一个已知名称的class的内部信息,包括modifiers(如public、static等)、superclass(如object)、实现的interfaces(如serializable)、fields(属性)和methods(方法)(但不包括methods定义),可于运行时改变fields的内容,也可调用methods -- 反射api: -- 获取类:class c=class.forname("alunbar");c.newinstance(); -- 获取类的方法信息,代码示例:class c=obj.getclass();method[] ms=c.getmethods();//参数列表获取省略 -- 获取类的成员变量信息,代码示例:class c=obj.getclass();field[] fs=c.getfields(); -- 获取类的构造方法信息信息,代码示例:class c=obj.getclass();constructor[] cs=c.getconstructors();//参数列表获取省略 -- 方法的反射调用,操作步骤: 1、根据方法的名称和参数列表获取唯一的方法; 2、拿到方法后,method.invoke(对象,参数列表)。 -- 配置文件 + 反射机制 实现开闭原则:在引入配置文件和反射机制后,需要更换或增加新的具体类将变得很简单,只需增加新的具体类并修改配置文件即可,无须对现有类库和客户端代码进行任何修改,完全符合开闭原则。在很多设计模式中都可以通过引入配置文件和反射机制来对客户端代码进行改进,如在抽象工厂模式中可以将具体工厂类类名存储在配置文件中,在适配器模式中可以将适配器类类名存储在配置文件中,在策略模式中可以将具体策略类类名存储在配置文件中等等。通过对代码的改进,可以让系统具有更好的扩展性和灵活性,更加满足各种面向对象设计原则的要求。 -- 小结:代码是现实世界的抽象,由于世界复杂的本质,注定不会有银弹。而java的反射机制为java提供一种编写足够抽象代码的手段 ·注解: 在编码过程中,为了更好的理解程序,我们通常会加上注释代码,这样可以方便程序员理解。而注解就是注释的一种,但是与一般的注释不同的是,注解需要按一定规则编写(见自定义注解),并且注解可以被程序解析,并根据解析结果进行相应处理。如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了。使用注解的过程中,很重要的一部分就是创建于使用注解处理器。java se5扩展了反射机制的api,以帮助程序员快速的构造自定义注解处理器。 ·元数据:指用来描述数据的数据。 ·元数据和注解:注解可以用来描述数据,所有注解是元数据的一种实现方式。 ·注解的作用:通过解析注解可以拿到注解中的属性和方法,并实现各式功能。 例如可以搭配反射实现开闭原则,因为注解可以被反射解析出来,此时的注解相当于一个配置文件。 ·在java中,除了注解充当配置文件,还可以用xml作为配置文件,但注解优点明显: 1、在class文件中,可以降低维护成本,annotation的配置机制很明显简单; 2、不需要第三方的解析工具,利用java反射技术就可以完成任务; 3、编辑期可以验证正确性,查错变得容易; 4、提高开发效率 ·ts中的注解:ts中其实没有注解的概念,但是前端界曾经还是有语言借鉴了注解:angular2的atscript语言,它能完完全全的单纯附加元数据。例如: @component({ selector: 'app' }) class appcomponent {} 等价于 class appcomponent {}
Support
Quality
Security
License
Reuse
Support
DesignPatterns_TypeScript has a low active ecosystem.
It has 20 star(s) with 6 fork(s). There are no watchers for this library.
It had no major release in the last 6 months.
There are 0 open issues and 1 have been closed. On average issues are closed in 26 days. There are no pull requests.
It has a neutral sentiment in the developer community.
The latest version of DesignPatterns_TypeScript is current.
Quality
DesignPatterns_TypeScript has no bugs reported.
Security
DesignPatterns_TypeScript has no vulnerabilities reported, and its dependent libraries have no vulnerabilities reported.
License
DesignPatterns_TypeScript is licensed under the MIT License. This license is Permissive.
Permissive licenses have the least restrictions, and you can use them in most projects.
Reuse
DesignPatterns_TypeScript releases are not available. You will need to build from source code and install.
Top functions reviewed by kandi - BETA
kandi's functional review helps you automatically verify the functionalities of the libraries and avoid rework.
Currently covering the most popular Java, JavaScript and Python libraries. See a Sample of DesignPatterns_TypeScript
Currently covering the most popular Java, JavaScript and Python libraries. See a Sample of DesignPatterns_TypeScript
DesignPatterns_TypeScript Key Features
No Key Features are available at this moment for DesignPatterns_TypeScript.
DesignPatterns_TypeScript Examples and Code Snippets
No Code Snippets are available at this moment for DesignPatterns_TypeScript.
Community Discussions
No Community Discussions are available at this moment for DesignPatterns_TypeScript.Refer to stack overflow page for discussions.
Community Discussions, Code Snippets contain sources that include Stack Exchange Network
Vulnerabilities
No vulnerabilities reported
Install DesignPatterns_TypeScript
You can download it from GitHub.
Support
For any new features, suggestions and bugs create an issue on GitHub.
If you have any questions check and ask questions on community page Stack Overflow .
Find more information at:
Reuse Trending Solutions
Find, review, and download reusable Libraries, Code Snippets, Cloud APIs from over 650 million Knowledge Items
Find more librariesStay Updated
Subscribe to our newsletter for trending solutions and developer bootcamps
Share this Page