UML类图【矩形框】代表一个类(Class)。类图分三层:
【线条】代表类之间的关系。 继承:空心三角形+实线,鸟类继承动物类 实现:空心三角形+虚线,鸟类实现飞翔接口 关联:实线箭头,企鹅类知道气候类(企鹅类中引用了气候对象) 聚合:空心菱形+实线箭头,每只大雁都是属于一个雁群,一个雁群可以有多只大雁(雁群中引用了大雁数组对象) 组合:实心菱形+实线箭头,翅膀是鸟的一部分,拥有相同的生命周期(初始化鸟对象时,同时实例化翅膀对象) 依赖:虚线箭头,动物需要氧气、水(氧气和水是动物类某个方法的参数) 六大原则 + 一个法则
1. 开放封闭原则开闭原则的意思是:对扩展开放,对修改关闭。软件实体(类、模块、函数等)应该可以扩展,但不可修改。即面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。 以计算器程序为例,抽象出一个运算类,吗,每当要增加新的运算方式时,只要修改这个抽象类和增加新的运算类,不会修改已有的运算类。 2. 单一职责原则单一职责原则,就一个类而言,应该仅有一个引起它变化的原因。 以俄罗斯方块为例,窗体显示(界面)是一个类,方块的移动控制(游戏逻辑)是另一个类,将不同的职责分离到不同的类上。 3. 里氏代换原则里氏替换原则:子类型必须能够替换掉它们的父类型。(多态) 里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。 4. 依赖倒转原则也叫依赖倒置、依赖反转。这个原则是开闭原则的基础。 依赖倒转原则,A)高层模块不应该依赖低层模块,两个都应该依赖抽象;b)抽象不应该依赖细节,细节应该依赖抽象。即针对接口名称,不要对实现编程。 例如,电脑可以很容易修理,因为电脑内部都是由一些(高内聚低耦合的)配件组成,坏了换一个新的就可;但是收音机很难修理,因为收音机里面都是一些元器件耦合在一起。 5. 接口隔离原则这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。 6. 合成复用原则合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。 7. 迪米特法则又称最少知道原则,是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。 如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法,可以通过第三者转发这个调用。 例如,公司小李去维修电脑,只需要去找IT部,让IT部去找具体的人来维修电脑。 设计模式1. 简单工厂模式
传入参数,由工厂对象去实例化实际的操作对象。 以计算器为例,传入操作运算符( 2. 策略模式定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户(多态)。 以超市打折为例,正常收费,打折收费和返利收费是具体策略,用一个类去管理具体策略;同时还可以与简单工厂模式结合,传入算法名称,由工厂去创建策略管理类。 【优点】
3. 装饰器模式装饰器模式,动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更加灵活。它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,在使用时要注意装饰的顺序。 以穿衣服为例,人为一个抽象类,有穿衣这个抽象方法,具体的男人类继承人抽象类,有一个抽象穿衣类(装饰器类)继承人抽象类,有具体的穿上衣类、穿外套类、穿内裤类、穿外裤类继承抽象穿衣类。先创建一个男人对象X、穿上衣对象A、穿内裤对象B等,将X设置到A中,再将A设置到B中,链式调用(注意是一层套一层的)。 4. 代理模式代理模式,为其它对象提供一种代理以控制对这个对象的访问。 例如,王小明通过戴励给刘小红送礼物,有 Subjec类:定义了RealSubject和Proxy的公用接口,这样在任何使用RealSubject的地方都可以使用Proxy。【送礼物这一行为】 RealSubject类:定义Proxy所代表的真实实体。【王小明】 Proxy类:保存一个引用使得代理可以访问真实实体,并提供一个与RealSubject内相同的访问方法,这样代理就可以用来替代真实实体。【戴励】 5. 工厂方法模式工厂模式方法,定义一个用于创建对象对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。 以计算器程序为例,创建一个工厂接口,然后加减乘除各建一个具体的工厂去实现这个接口,每个具体工厂负责创建具体的运算对象。 6. 原型模式原型模式,用原型实例指定创建对象的种类,并通过拷贝这些原型对象创建新的对象。 例如,有一个抽象原型类有抽象clone方法,然后有多个具体原型类去实现该clone方法,注意【浅拷贝与深拷贝】,原型中引用的对象,拷贝的时候只会拷贝引用,需要在引用的对象中也实现拷贝方法,然后在给原型赋值的是对象的拷贝。 7. 模板方法模式定义一个操作中的算法的骨架,将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 比如,一个抽象动物类有抽象吃食物的方法,在一些模板方法可以调用这个抽象方法(例如吃完东西才会睡觉),具体的狗类、猫类、鸟类实现具体的吃食物的方法。 8. 外观模式外观模式,为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
例如股民炒股,股民需要知道多个股票的情况,耦合性过高,而把钱投在基金上,由基金来管理多支股票,用户只用与基金打交道。 【何时使用】
9. 建造者模式建造者模式(Builder),又叫生成器模式,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 例如,一个画小人程序,可以画出瘦人、胖人等,有 Product类:小人类,由多个部分组成。 Builder接口:建造小人各个部分的抽象类。 ConcreteBuilder类:具体建造者,实现Builder接口。具体实现如何画出小人的头身手脚各个部分。 Director类:指挥者,构建一个使用Builder接口的对象。用来根据用户的需求构建小人对象。 10. 观察者模式观察者模式,又叫发布-订阅(Publish/Subscribe)模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。 例如,员工上班偷懒,需要前台A或者前台B望风,有 Subject类:抽象通知者类(主题)。一般用一个抽象类或者一个接口实现。它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。 Observer类:抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者一般用一个抽象类或者一个接口实现。更新接口通常包含一个Update() 方法,这个方法叫做更新方法。 ConcreteSubject类:叫做具体主题或具体通知者,将有关状态存入具体现察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。 ConcreteObserver类:具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。具体观察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。
11. 抽象工厂模式抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。解决涉及到多个产品系列的问题,有一个专门的工厂模式叫抽象工厂模式。 例如,有一个业务需要访问指定的数据库(可能会变动),数据库中又有几张表,为了便于修改数据库访问方式,这时就需要使用抽象工厂模式。 抽象工厂接口:里面应该包含所有的产品创建的抽象方法【创建数据库的抽象方法】; 具体的工厂:实现抽象工厂接口,创建具有特定实现的产品对象【实例化对应的具体数据库对象】; 抽象产品:它们都有可能有多种不同的实现【数据库的访问资源方式的抽象方法】; 具体产品:继承抽象产品,对抽象产品的具体分类的实现【具体数据库的访问资源方法】。 【改进1】反射 Java反射:通过全限定类名去加载对应类的字节码(Class)文件
反射中的类名是字符串,可以采用变量,故更容易修改为不同的数据库具体产品,且不用在主程序中写switch方法。 使用DataAccess类,用反射技术,取代IFactory、SqlserverFactory和AccessFactory。 【改进2】反射 + 配置文件 将数据库名称放在配置文件中,这样只需要修改配置文件就可以修改程序的访问数据库,不用去改程序代码。 【总结】从这个角度上说,所有在用简单工厂的地方,都可以考虑用反射技术来去除switch或if,解除分支判断带来的耦合。 12. 状态模式状态模式(State),当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。 比如,上班时不同时间做不同工作就可以使用状态模式,首先有一个抽象状态类,设置多个工作状态继承抽象状态类,有一个工作类(上下文)来负责转换工作转态,传入任意一个工作状态,当满足某条件的时候,转入下一工作状态。 13. 适配器模式适配器模式(Adapter),将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 适配器模式有两种类型,类适配器模式和对象适配器模式。类适配器通过多重继承对一个接口与另一个接口进行匹配。对象适配器模式主要结构如下: Target:客户期待的接口 Adaptee:需要适配的类 Adapter:实现Target接口,通过在内部包装一个Adaptee对象,把源接口转换成目标接口 14. 备忘录模式备忘录(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。 例如,有一个游戏角色,可以在战斗之前保存状态,战斗失败了可以恢复到之前保存的状态。有 Originator类:发起人(游戏角色)。负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。Originator 可根据需要决定Memento存储Originator的哪些内部状态。 Memento类:备忘录(角色状态)。负责存储Originator对象的内部状态,并可防止Originator 以外的其他对象访问备忘录Memento。备忘录有两个接口,Caretaker只能看到备忘录的窄接口,它只能将备忘录传递给其他对象。Originator能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。 Caretaker类:管理者(负责管理状态)。负责保存好备忘录Memento(Originator创建的备忘录存在Caretaker里面),不能对备忘录的内容进行操作或检查。
15. 组合模式组合模式(Composite),将对象组合成树形结构以表示部分-整体的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 比如,一个总公司有财务部、技术部、人力资源部等,还有分公司,分公司同样有财务部、技术部、人力资源部等。可以先设计一个公司的抽象类(或者接口),里面有增加、移除、显示部门、履行职责的抽象方法,然后设计具体的公司类继承该抽象类,实现这些方法,并加上children相关的方法;部门类也去继承公司抽象类,实现这些方法。创建分公司的时候同创建总公司相同。 这样,基本对象可以被组合为更复杂的组合对象,这个组合对象又可以被组合。 16. 迭代器模式迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。 当你需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,你就应该考虑用迭代器模式。当你需要对聚集有多种方式遍历时,可以考虑用迭代器模式。Java中的 迭代器模式就是分离了集合对象的遍历行为,抽象出一个选代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。迭代器模式在访问数组、集合、列表等数据时,尤其是数据库数据操作时,是非常普遍的应用,但由于它太普遍了,所以各种高级语言都对它进行了封装。 17. 单例模式单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。 让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法(静态方法)。
一般采用饿汉式在类直接创建静态对象:
如果明确需要使用懒加载,可以采用登记式/静态内部类方式:
否则使用双检锁/双重校验锁方式:
18. 桥接模式桥接模式(Bridge),将抽象部分与它的实现部分分离,使它们都可以独立地变化。(实现指的是抽象类和它的派生类用来实现自己的对象)。 实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。 例如为不同的手机开发游戏,可以将手机硬件和软件分离,手机有A、B两个品牌,软件有M、N两类。有一个抽象手机类,A手机、B手机继承抽象手机类;有一个抽象软件类,M软件、N软件继承抽象软件类;然后抽象手机类引用抽象软件类。这样不管增加手机还是增加软件都很方便。
19. 命令模式命令模式(Command),将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。 例如点烧烤,我们是客户端,服务员是Invoker,向厨师Receiver发送命令,其中有一个抽象的Command类用于声明执行操作的接口,ConcreteCommand类继承Command类,(绑定接收者厨师Receiver)用于实现具体的动作。服务员Invoke是通过Command类来向厨师Receiver进行交互的。 【优点】
20. 职责链模式职责链模式(Chain of Responsibility),使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。 比如,员工请假,不同管理者级别能处理的请假天数不一样。设置一个管理者抽象类,有设置上级(继任者)方法和处理请求的抽象方法,有多个级别的管理者继承该抽象类,实现处理请求的抽象方法。这样,员工(客户端)每次都是向组长请假,组长批不了的就交由部门经理处理,经理处理不了的就由总监处理。 21. 中介者模式中介者模式(Mediator),用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 例如,联合国安理会调节国家冲突。有一个联合国抽象类,里面有调节抽象方法(参数为调节信息和调节对象),有具体的安理会继承联合国抽象类,实现具体方法;有国家抽象类,需要设置中介者,有具体国家类继承国家抽象类,实现具体方法;在实际使用时,由中介者去调用国家类的方法。 中介者模式一般应用于一组对象以定义良好但是复杂的方式进行通信的场合,比如刚才得到的窗体Form对象或Web页面aspx,以及想定制一个分布在多个类中的行为,而又不想生成太多的子类的场合。 【优点】
【缺点】
22. 享元模式享元模式(Flyweight),运用共享技术有效地支持大量细粒度的对象。 比如为不同人创建类似的网站,可以把网站的公共部分抽取出来,将用户设置为外部状态传进去。这样的话,就算要为10个用户创建网站,也可以只有一个网站实例。有用户类,有一个抽象网站类,里面有抽象使用方法(传入用户),有多个具体的网站类,实现抽象方法;有一个网站工厂负责管理网站(没有则创建,有则取出返回)。 如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。 【优点】
【缺点】
23. 解释器模式解释器模式(interpreter),给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。 比如正则表达式,可以用特定的文法去匹配字符串。 AbstractExpression类:抽象表达式。声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。 TerminalExpression类:终结符表达式。实现与文法中的终结符相关联的解释操作。实现抽象表达式中所要求的接口,主要是一个interpret() 方法。文法中每一个终结符都有一个具体终结表达式与之相对应。 NonterminalExpression类:非终结符表达式,为文法中的非终结符实现解释操作。对文法中每一条规则 R1、R2……Rn 都需要一个具体的非终结符表达式类。通过实现抽象表达式的interpret() 方法实现解释操作。解释操作以递归方式调用上面所提到的代表R1、R2……Rn中各个符号的实例变量。 Context类:包含解释器之外的一些全局信息。 24. 访问者模式访问者模式(Visitor),表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。 比如,有男人、女人2种数据结构,每类人都有多个情绪,情绪对应的操作不同。有抽象访问者类Visitor,为该对象结构中ConcreteElement的每一个类声明一个Visit操作;ConcreteVisitor1和ConcreteVisitor2类,具体访问者,实现每个由Visitor声明的操作;人抽象类Element,定义一个Accept操作,它以一个访问者为参数;男人类 ConcreteElementA和女人类ConcreteElementB,具体元素,实现Accept操作;人的集合ObjectStructure类,能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素。
访问者模式适用于数据结构相对稳定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。 【优点】
【缺点】
设计模式总结[23种]1. 创建型模式
抽象工厂模式:提供一个创建一系列或相关依赖对象的接口,而无需指定它们具体的类。 建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂模式使一个类的实例化延迟到其子类。 原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
2. 结构型模式
适配器模式:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。 组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。 装饰器模式:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更加灵活。 外观模式:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 享元模式:运用共享技术有效地支持大量细粒度的对象。 代理模式:为其他对象提供一种代理以控制对这个对象的访问。 3. 行为型模式
观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 模板方法模式:定义一个操作的算法骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;可以对请求排队或记录请求日志,以及支持可撤销的操作。 状态模式:允许一个对象在其内部状态改变时改变它的行为,让对象看起来似乎修改了它的类。 职责链模式:多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。 解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。 中介者模式:一个中介对象来封装一系列的对象交互。中介者使各对像不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 访问者模式:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。 策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换,使得算法可独立于使用它的客户而变化。 备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。 迭代器模式:提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。 |
|