JAVA SCRIPT设计模式-开篇

一、前言

        JAVA SCRIPT设计模式是本人根据GOF的设计模式写的博客记录。使用JAVA SCRIPT语言来实现主体功能,所以不可能像C++,JAVA等面向对象语言一样严谨,大部分程序都附上了JAVA SCRIPT代码,代码只是实现了设计模式的主体功能,不代表全部的正确,特此声明。

二 、设计模式与变化

2.1 程序设计应支持变化

         获得最大限度复用的关键在于对新需求和已有需求发生变化时的预见性,要求你的系统设计要能够相应地改进。

        为了设计适应这种变化、且具有健壮性的系统,你必须考虑系统在它的生命周期内会发生怎样的变化。一个不考虑系统变化的设计在将来就有可能需要重新设计。这些变化可能是类的重新定义和实现,修改客户和重新测试。重新设计会影响软件系统的许多方面,并且未曾料到的变化总是代价巨大的。 设计模式可以确保系统能以特定方式变化,从而帮助你避免重新设计系统。每一个设计模式允许系统结构的某个方面的变化独立于其他方面,这样产生的系统对于某一种特殊变化将更健壮。

        下面阐述了一些导致重新设计的一般原因,以及解决这些问题的设计模式:

序号原因内容表现解决方法解决的设计模式
一、通过显式地指定一个类来创建对象在创建对象时指定类名将使你受特定实现的约束 而不是特定接口的约束。这会使未来的变化更复杂。要避免这种情况,应该间接地创建对象。

Abstract Factory 

Factory Method

Prototype

抽象工厂

工厂方法

原型

二、对特殊操作的依赖当你为请求指定一个特殊的操作时,完成该请求的方式就固定下来了。为避免把请求代码写死,你将可以在编译时刻或运行时刻很方便地改变响应请求的方法。

Chain of Resposibility

Command

职责链

命令

三、对硬件和软件平台的依赖外部的操作系统接口和应用编程接口 ( A P I )在不同的软硬件平台上是不同的。依赖于特定平台的软件将很难移植到其他平台上,甚至都很难跟上本地平台的更新。所以设计系统时限制其平台相关性就很重要了。

Abstract Factory

Bridge

抽象工厂

桥接

四、对对象表示或实现的依赖知道对象怎样表示、保存、定位或实现的客户在对象发生变化时可能也需要变化。对客户隐藏这些信息能阻止连锁变化。

Abstract Factory

Bridge

Memento

Proxy

抽象工厂

桥接

备忘录

代理 

五、算法依赖算法在开发和复用时常常被扩展、优化和替代。依赖于某个特定算法的对 象在算法发生变化时不得不变化。因此有可能发生变化的算法应该被孤立起来。

Builder 

Iterator

Strategy

Template Method

Visitor

生成器

迭代器

策略

模板方法

访问者

六、紧耦合紧耦合的类很难独立地被复用,因为它们是互相依赖的。紧耦合产生单块的 系统,要改变或删掉一个类,你必须理解和改变其他许多类。松散耦合提高了一个类本身被复用的可能性,并且系统更易于学习、移植、修改和扩展。 设计模式使用抽象耦合和分层技术来提高系统的松散耦合性。

Abstract Factory

Command

Facade 

Mediator

Observer

Chain of Resposibility

抽象工厂

命令

外观

中介者

观察者

职责链

七、通过生成子类来扩充功能通常很难通过定义子类来定制对象。每一个新类都有固定 的实现开销(初始化、终止处理等)。定义子类还需要对父类有深入的了解。如,重定义一个操 作可能需要重定义其他操作。一个被重定义的操作可能需要调用继承下来的操作。并且子类 方法会导致类爆炸,因为即使对于一个简单的扩充,你也不得不引入许多新的子类。一般的对象组合技术和具体的委托技术,是继承之外组合对象行为的另一种灵活方法。 新的功能可以通过以新的方式组合已有对象,而不是通过定义已存在类的子类的方式加到应 用中去。另一方面,过多使用对象组合会使设计难于理解。许多设计模式产生的设计中,你 可以定义一个子类,且将它的实例和已存在实例进行组合来引入定制的功能。

Bridge

Chain of Responsibility

Composite

Decorator

Observer

Strategy

桥接

职责链

组合

装饰

观察者

策略

八、不能方便地对类进行修改有时你不得不改变一个难以修改的类。也许你需要源代码而又没有(第三方库 ),或者可能对类的任何改变会要求修改许多已存在的其他子类。设计模式提供在这些情况下对类进行修改的方法。

Adapter

Decorator

Visitor

适配器

装饰

访问者

2.2 设计模式目录及可变方面

目的分类 模式中文简称设计模式可变方向意图
创建类抽象工厂Abstract Factory对产品对象家族提供一个创建一系列相关或相互依赖对象的接口,而无需指定它 们具体的类。
创建对象生成器Builder 对如何创建一个组合对象将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不 同的表示。
创建对象工厂方法Factory Method对被实例化的子类定义一个用于创建对象的接口,让子类决定将哪一个类实例化。 使一个类的实例化延迟到其子类。
创建对象原型Prototype对被实例化的类用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对 象。
创建对象单例Singleton对一个类的唯一实例保证一个类仅有一个实例,并提供一个访问它的全局访问点。

结构类

结构对象

适配器Adapter对对象的接口将一个类的接口转换成客户希望的另外一个接口。 使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
结构对象桥接Bridge对对象的实现将抽象部分与它的实现部分分离,使它们都可以独立地变化。
结构对象组合Composite对一个对象的结构和组成将对象组合成树形结构以表示“部分 -整体”的层次结构。使得客户对单个对象和复合对象的使用具有一致性。
结构对象装饰Decorator对对象的职责,不生成子类动态地给一个对象添加一些额外的职责。就扩展功能而言,比生成子类方式更为灵活。
结构对象外观Facade对一个子系统的接口为子系统中的一组接口提供一个一致的界面, 定义了一个高层 接口,这个接口使得这一子系统更加容易使用。
结构对象享元Flyweight对对象的存储开销运用共享技术有效地支持大量细粒度的对象。
结构对象代理Proxy对如何访问一个对象;该对象的位置为其他对象提供一个代理以控制对这个对象的访问。
行为对象职责链Chain of Responsibility对满足一个请求的对象为解除请求的发送者和接收者之间耦合,而使多个对象都 有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象 处理它。
行为对象命令Command对何时、怎样满足一个请求将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数 化;对请求排队或记录请求日志,以及支持可取消的操作。
行为类解释器Interpreter对一个语言的文法及解释给定一个语言 , 定义它的文法的一种表示,并定义一个解释器 , 该解释 器使用该表示来解释语言中的句子
行为对象迭代器Iterator对如何遍历、访问一个聚合的各元素提供一种方法顺序访问一个聚合对象中各个元素 , 而又不需暴露该对象的 内部表示。
行为对象中介者Mediator对对象间怎样交互、和谁交互用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式 地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
行为对象备忘录Memento对一个对象中哪些私有信息存放在该对象之外,以及在对什么时候进行存储在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外 保存这个状态。这样以后就可将该对象恢复到保存的状态。
行为对象观察者Observer对多个对象依赖于另外一个对象,而这些对象又如何保 持一致定义对象间的一种一对多的依赖关系 ,以便当一个对象的状态发生改变时 , 所有依赖于它的对象都得到通知并自动刷新。
行为对象状态State对对象的状态允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它 所属的类。
行为对象策略Strategy对算法定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模 式使得算法的变化可独立于使用它的客户。
行为类模板方法Template Method对算法中的某些步骤定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。 Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
行为对象访问者Visitor对某些可作用于一个(组)对象上的操作,但不修改这些对象的类表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元 素的类的前提下定义作用于这些元素的新操作。

        模式依据其目的可分为创建型 (Creational)、结构型 ( Structural )、或 行为型 ( Behavioral )三种。创建型模式与对象的创建有关;结构型模式处理类或对象的组合;行为型 模式对类或对象怎样交互和怎样分配职责进行描述。 类模式处理类和子类之间的关 系,这些关系通过继承建立,是静态的,在编译时刻便确定下来了。对象模式处理对象间的 关系,这些关系在运行时刻是可以变化的,更具动态性。

        创建型类模式将对象的部分创建工作延迟到子类,而创建型对象模式则将它延迟到另一个对象中。结构型类模式使用继承机制来组合类,而结构型对象模式则描述了对象的组装方式。行为型类模式使用继承描述算法和控制流,而行为型对象模式则描述一组对象怎样协作 完成单个对象所无法完成的任务。

2.3 设计模式之间的关系

 2.4 运用复用机制

        理解对象、接口、类和继承之类的概念对大多数人来说并不难,问题的关键在于如何运 用它们写出灵活的、可复用的软件。设计模式将告诉你怎样去做。

        第一个原则:针对接口编程,而不是针对实现编程

        第二个原则:  优先使用对象组合,而不是类继承。

2.4.1  继承和组合的比较

        面向对象系统中功能复用的两种最常用技术是类继承和对象组合(object composition)。类继承允许你根据其他类的实现来定义一个类的实现。这种通过生成子
类的复用通常被称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,父类的内部细节对子类可见。
        对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用
(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。

        继承和组合各有优缺点。类继承是在编译时刻静态定义的,且可直接使用,因为程序设计语言直接支持类继承。类继承可以较方便地改变被复用的实现。当一个子类重定义一些而
不是全部操作时,它也能影响它所继承的操作,只要在这些操作中调用了被重定义的操作。
        但是类继承也有一些不足之处。首先,因为继承在编译时刻就定义了,所以无法在运行时刻改变从父类继承的实现。更糟的是,父类通常至少定义了部分子类的具体表示。因为继
承对子类揭示了其父类的实现细节,所以继承常被认为“破坏了封装性” 。子类中的实现与它的父类有如此紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化

        当你需要复用子类时,实现上的依赖性就会产生一些问题。如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。一个可用的解决方法就是只继承抽象类,因为抽象类通常提供较少的实现。
        对象组合是通过获得对其他对象的引用而在运行时刻动态定义的。组合要求对象遵守彼此的接口约定,进而要求更仔细地定义接口,而这些接口并不妨碍你将一个对象和其他对象一起使用。这还会产生良好的结果:因为对象只能通过接口访问,所以我们并不破坏封装性;只要类型一致,运行时刻还可以用一个对象来替代另一个对象;更进一步,因为对象的实现是基于接口写的,所以实现上存在较少的依赖关系。
        对象组合对系统设计还有另一个作用,即优先使用对象组合有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控
制的庞然大物。另一方面,基于对象组合的设计会有更多的对象 (而有较少的类),且系统的行为将依赖于对象间的关系而不是被定义在某个类中。这导出了我们的面向对象设计的第二个原则:优先使用对象组合,而不是类继承。
        理想情况下,你不应为获得复用而去创建新的构件。你应该能够只使用对象组合技术,通过组装已有的构件就能获得你需要的功能。但是事实很少如此,因为可用构件的集合实际上并不足够丰富。使用继承的复用使得创建新的构件要比组装旧的构件来得容易。这样,继承和对象组合常一起使用。 然而,我们的经验表明:设计者往往过度使用了继承这种复用技术但依赖于对象组合技术的设计却有更好的复用性 (或更简单)。你将会看到设计模式中一再使用对象组合技术。

2.4.2  委托

        委托(delegation)是一种组合方法它使组合具有与继承同样的复用能力。在委托方式下,有两个对象参与处理一个请求,接受请求的对象将操作委托给它的代理者(delegate)。这类似于子类将请求交给它的父类处理。使用继承时,被继承的操作总能引用接受请求的对象。委托方式为了得到同样的效果,接受请求的对象将自己传给被委托者(代理人),使被委托的操作可以引用接受请求的对象。


        举例来说,我们可以在窗口类中保存一个矩形类的实例变量来代理矩形类的特定操作,这样窗口类可以复用矩形类的操作,而不必像继承时那样定义成矩形类的子类。也就是说,
一个窗口拥有一个矩形,而不是一个窗口就是一个矩形。窗口现在必须显式的将请求转发给它的矩形实例,而不是像以前它必须继承矩形的操作。

        下面的图显示了窗口类将它的Area操作委托给一个矩形实例。

 
        箭头线表示一个类对另一个类实例的引用关系。引用名是可选的,本例为“rectangle”。委托的主要优点在于它便于运行时刻组合对象操作以及改变这些操作的组合方式。假定矩形对象和圆对象有相同的类型,我们只需简单的用圆对象替换矩形对象,则得到的窗口就是圆形的。
        委托与那些通过对象组合以取得软件灵活性的技术一样,具有如下不足之处:动态的、高度参数化的软件比静态软件更难于理解。还有运行低效问题,不过从长远来看人的低效才是更主要的。只有当委托使设计比较简单而不是更复杂时,它才是好的选择。要给出一个能确切告诉你什么时候可以使用委托的规则是很困难的。因为委托可以得到的效率是与上下文有关的,并且还依赖于你的经验。委托最适用于符合特定程式的情形,即标准模式的情形。
        有一些模式使用了委托,如State(5.8)、Strategy(5.9)和Visitor(5.11)。在State模式中,一个对象将请求委托给一个描述当前状态的State对象来处理。在Strategy模式中,一个对象将一个特定的请求委托给一个描述请求执行策略的对象,一个对象只会有一个状态,但它对不同的请求可以有许多策略。这两个模式的目的都是通过改变受托对象来改变委托对象的行为。在Visitor中,对象结构的每个元素上的操作总是被委托到Visitor对象。
        其他模式则没有这么多地用到委托。Mediator(5.5)引进了一个中介其他对象间通信的对象。有时,Mediator对象只是简单地将请求转发给其他对象;有时,它沿着指向自己的引用来传递请求,使用真正意义的委托。Chain of Responsibility(5.1)通过将请求沿着对象链传递来处理请求,有时,这个请求本身带有一个接受请求对象的引用,这时该模式就使用了委托。Bridge(4.2)将实现和抽象分离开,如果抽象和一个特定实现非常匹配,那么这个实现可以代理抽象的操作。委托是对象组合的特例。它告诉你对象组合作为一个代码复用机制可以替代继承。

三、设计模式之六大原则

3.1 开闭原则(Open Close Principle)

         在面向对象编程领域中,开闭原则规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。该特性在产品化的环境中是特别有价值的,在这种环境中,改变源代码需要代码审查,单元测试以及诸如此类的用以确保产品使用质量的过程。遵循这种原则的代码在扩展时并不发生改变,因此无需上述的过程。

        参考:开闭原则_百度百科 (baidu.com)

3.2 里氏代换原则(Liskov Substitution Principle)

        里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

        参考:里氏代换原则_百度百科 (baidu.com)

3.3 依赖倒转原则(Dependence Inversion Principle)

        依赖倒置原则(Dependence Inversion Principle)是程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。

        面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本。
        面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度。

        参考:依赖倒置原则_百度百科 (baidu.com)

3.4 接口隔离原则(Interface Segregation Principle)

        客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上。  

        使用多个专门的接口比使用单一的总接口要好。

        一个类对另外一个类的依赖性应当是建立在最小的接口上的。

        一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染。

        “不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构。”这个说得很明白了,再通俗点说,不要强迫客户使用它们不用的方法,如果强迫用户使用它们不使用的方法,那么这些客户就会面临由于这些不使用的方法的改变所带来的改变。

        参考:接口隔离原则_百度百科 (baidu.com)

3.5 迪米特法则,又称最少知道原则(Demeter Principle)

        迪米特法则(Law of Demeter)又叫作最少知识原则(The Least Knowledge Principle),一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话。英文简写为: LOD。

        迪米特法则可以简单说成:talk only to your immediate friends。 对于OOD来说,又被解释为下面几种方式:一个软件实体应当尽可能少的与其他实体发生相互作用。每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。

        迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。迪米特法则不希望类之间建立直接的联系。如果真的有需要建立联系,也希望能通过它的友元类来转达。因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系——这在一定程度上增加了系统的复杂度。

        有兴趣可以研究一下设计模式的门面模式(Facade)和中介模式(Mediator),都是迪米特法则应用的例子。

        参考:迪米特法则_百度百科 (baidu.com)

3.6 合成复用原则(Composite Reuse Principle)

        就是说要少用继承,多用合成关系来实现。我曾经这样写过程序:有几个类要与数据库打交道,就写了一个数据库操作的类,然后别的跟数据库打交道的类都继承这个。结果后来,我修改了数据库操作类的一个方法,各个类都需要改动。"牵一发而动全身"!面向对象是要把波动限制在尽量小的范围。

        在Java中,应尽量针对Interface编程,而非实现类。这样,更换子类不会影响调用它方法的代码。要让各个类尽可能少的跟别人联系,"不要与陌生人说话"。这样,城门失火,才不至于殃及池鱼。扩展性和维护性才能提高

        其他参考:软件设计模式_百度百科 (baidu.com)

四 、MVC与设计模式

        MVC包括三类对象。模型Model是应用对象,视图View是它在屏幕上的表示,控制器Controller定义用户界面对用户输入的响应方式。不使用MVC,用户界面设计往往将这些对象混在一起,而MVC则将它们分离以提高灵活性和复用性。

4.1 视图和模型


       MVC通过建立一个“订购/通知”协议来分离视图和模型。视图必须保证它的显示正确地反映了模型的状态。一旦模型的数据发生变化,模型将通知有关的视图,每个视图相应地得到刷新自己的机会。这种方法可以让你为一个模型提供不同的多个视图表现形式,也能够为一个模型创建新的视图而无须重写模型。

4.2 视图的组合

       MVC的另一个特征是视图可以嵌套。例如,按钮控制面板可以用一个嵌套了按钮的复杂视图来实现。对象查看器的用户界面可由嵌套的视图构成,这些视图又可复用于调试器。MVC用View类的子类—CompositeView类来支持嵌套视图。CompositeView类的对象行为上类似于View类对象,一个组合视图可用于任何视图可用的地方,但是它包含并管理嵌套视图。上例反映了可以将组合视图与其构件平等对待的设计,同样地,该设计也适用于更一般的问题:将一些对象划为一组,并将该组对象当作一个对象来使用。这个设计被描述为Composite(4.3)模式,该模式允许你创建一个类层次结构,一些子类定义了原子对象(如Button)而其他类定义了组合对象(CompositeView),这些组合对象是由原子对象组合而成的更复杂的对象。

4.3 视图和控制器


        MVC允许你在不改变视图外观的情况下改变视图对用户输入的响应方式。例如,你可能希望改变视图对键盘的响应方式,或希望使用弹出菜单而不是原来的命令键方式。MVC将响应机制封装在Controller对象中。存在着一个Controller的类层次结构,使得可以方便地对原有Controller做适当改变而创建新的Controller。View使用Controller子类的实例来实现一个特定的响应策略。要实现不同的响应策略只要用不同种类的Controller实例替换即可。甚至可以在运行时刻通过改变View的Controller来改变View对用户输入的响应方式。例如,一个View可以被禁止接收任何输入,只需给它一个忽略输入事件的Controller。
        View-Controller关系是Strategy(5.9)模式的一个例子。一个策略是一个表述算法的对象。当你想静态或动态地替换一个算法,或你有很多不同的算法,或算法中包含你想封装的复杂
数据结构,这时策略模式是非常有用的。


        MVC还使用了其他的设计模式,如:用来指定视图缺省控制器的FactoryMethod(3.3)和用来增加视图滚动的Decorator(4.4)。但是MVC的主要关系还是由Observer、Composite和Strategy三个设计模式给出的。

五 、开发工具和环境搭建

5.1 开发工具和环境搭建

        工具和环境的搭建可以参考这里

5.2 打包

        可以用webpack进行打包,也可以使用ROLLUP.JS进行处理。ROLLUP.JS的打包可以参考这里

六、图书资源和源代码下载

        下载链接:https://pan.baidu.com/s/1XuPqp84cccBNVkbnMY3sKw 
         提取码:q2ut

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值