概述
为啥要看
- 项目更好维护
- 实现更优雅
- 读源码更轻松
- 理解面向对象
设计模式是解决很多问题的更优雅的实现,很多项目难以维护或者难以拓展都是因为设计的时候很多没有做好。
当然这里一开始只有最简单的 GoF23种设计模式,后面会考虑将Spring或者其他源码里面抽出,结合设计模式来讲,可能会更好一些
很多优秀的开源组件框架都大量用到设计模式,很多时候你看不懂,或者读起来也就那样,没有那种原来如此,令你眼前一亮的感觉。其实就是设计模式你还没有理解。比如我一开始看Spring的Ioc实现原理,我根本没有看出来他哪里优秀了。怎么就来了个革命性的成果呢(我当时没有读设计模式,代码写的也少)
现在看,跟以前看是不一样的,不过仍然还是没有抓住他的要点。毕竟设计模式的思想真的他高深了
学习前应当注意
抽象类和接口的区别
- 抽象类中的方法可以是具体类,也可以是抽象类(具体可以看Tamplate模式的使用)。接口全部是抽象类,且公共
- 抽象类需要继承,接口需要实现。这两个的区别主要在 父类多的情况下,Java继承只能单继承,但可以实现多个接口。(比如我们在Adapter模式的时候,他有两种实现方式,一种继承,一种委托。一个使用接口,一个使用抽象类)
- 欢迎补充,暂时想到这么多。之前想到了没有记下来,忘完了
使用抽象类和接口的目的
- 声明和实现分离,便于直观了解
- 弱化各个程序间的耦合,实现可复用
- 各个处理流程在抽象类阶段确立
- 如果传入的是他们两个的父类,调用者并不需要知道具体的是哪一个子类,(参考Composite模式,其实基本上大部分都会体现,如第一个Iterator模式,在我们BookShelf调用BookShelfIterator的时候,用于返回的是Iterator,而不是BookShelfIterator。不过Composite模式更直观)
面向对象
在我第一个节点,为啥要看设计模式的时候有一个理由写道,理解面向对象。很多人可能会不太服,我学了面向对象语言(如Java)那么久,早就已经掌握了面向对象的思想。从开始学Java的时候,就天天提到面向对象的三大特性,封装、继承、多态。傻子都背熟了,面向对象也没啥难的。
真的是这样吗?我们说到Java是一个面向对象的语言,皆可对象,所有的都可以当作对象来处理,就连8个基本类型,都有他们的装箱类。我们只要将东西抽象成对象就行了。是这样吗?
如果你只是按照上面的来想,那真的就太太太小看面向对象了。
对象
如果按照上面的逻辑理解面向对象,那么C语言也可以称为面向对象的语言了。因为他可以定义 Struct,结构体,和Java这种面向对象语言的样子很类似。所以这个并不能使一个语言成为面向对象的语言
关键还是忽略了,面向对象的强大的后面两个——继承、多态。我入职第一周,写程序。写出来是面向过程的,老大让我把程序改成面向对象的。然后我就定义了几个类,将变量值改成对象的属性。(想想还是憨)
我们老大说,你这不还是面向过程吗?面向对象体现在哪里? 连个继承和多态都没有。
后来我定义了个抽象父类,勉勉强强算是打成了改成面向对象的目标。
所以,仅仅只有类和对象,是不能被称为面向对象的
继承、多态
继承是多态的前提条件。只有有了继承,并且重写了父类的方法,才会有多态
但是我们一写到程序里,往往就不会想着使用这两个。因为面向过程更符合我们的思考方式,想到啥逻辑,一气呵成就写完了
但是那样不好维护,这种只有等你真正实战才能体会,光纸上谈兵是根本体会不了的
多态的实质是Java的动态分派,如果有兴趣,可以看下我的JVM中的这篇文章。重写和重载的区别
抽象
抽象这个概念很不好理解,因为他本来就很抽象
我们把现实中的物体的某些事物的共同特征找出来,然后总结一下,这就是抽象的过程。
比如我们本子,盒子,书这些生活中具体的事物,我们找到他的共同特征,都有12个点,8条棱,6个面。并且相对的棱都是平行的,相对的面也是平行的。然后我们就抽象出了一个图形——长方体。长方体就是我们抽象出来的东西。我们不讲究他的颜色、材质、重量等等。
第一步 普通类:
这个过程就像我们创建类一样,找到公共的属性,然后进行抽象,变成一个类。
这是抽象的第一步,创建具体类
第二步 抽象类
我们在抽象的基础上再进行抽象。抽象的东西还能再抽象吗?当然可以
再往上抽象就变成了 抽象类,抽象类因为比类还抽象,所以他不能直接实例化,但是可以被其他具体子类继承
这个抽象类里面,有两种方法
- 抽象方法,只声明,但是不实现。
- 普通方法,普通类中的方法一样,需要具体实现
但是这两个方法都可以被子类重写,从而实现 多态
第三步 接口
我们还可以再抽象。我们属性都不要。就只要方法(函数、行为),并且这些方法都必须是抽象方法
这就是第三步的抽象,接口
第四步 设计模式
还能再抽象?类方面我们不能再抽象了,不过我们能把这些组合抽象一下。
于是,设计模式就诞生了。著名的设计模式,是GoF总结的23种。当然,不止GoF这一种,也不是他们发明创造的,他们只是将大家做的总结出来了而已。而且23种设计模式还只是基础。
我们实际工作中可能还要用到多种设计模式组合出的东西
所以如果学习设计模式,你不了解 抽象类和接口的妙用,是根本就没有懂设计模式
如果不懂基础的23种设计模式,那你可能连面向对象的门都没入
关于抽象类还有接口,我现在说,不懂的还是不懂,我只能在各个设计模式中给大家讲我的分享
怎么学
我只能分享一下我的学习方法,不一定是最优的,但可以作为参考
首先拿到这本书我是懵的,因为这个东西太抽象了。学编程都知道,很多东西都是抽象抽象再抽象。而设计模式更是将之前的面向对象又往上抽象了一下。所以新手一开始不懂非常正常,我看了两三遍才勉强看懂为啥这样做
第一阶段
- 你可能看不懂代码为啥这样写,也没必要一定要搞懂为啥这个设计模式要这样。但是一定要把代码敲一遍
- 看清楚哪些用了接口,哪些用了抽象类
- 然后看12个设计模式,先看一半。
第二阶段
- 你已经看了一半,基本已经有一些想法了。知道接口和抽象类的不同和好处
- 然后回头重新看,并且再把代码跟着敲一遍,这个时候要想一下,他们是怎么调用的,有啥好处
- 再想一下哪里用到了相似的,并且再看到一半
第三阶段
- 已经看了两遍,代码也敲了两遍。基本每个模式的例子都有印象了
- 跟着他的 UML 图试着还原他的设计模式
- 找出那些设计模式很相似,或者这个设计模式嵌套了另一个设计模式(比如Tamplate模式被非常多的模式都用了,有哪些,为啥这样做)
第四阶段
- 找到相关的源码,比如 线程池用到了 工厂模式。看他怎么使用的工厂模式
- 最好结合实际来。比如我之前就被老大叫去使用单例写一个小工具。之前改程序的时候也用到Tamplate模式
这样子下来,基本算是稍微掌握了设计模式,更多的就需要自己在项目中刻意练习了
各个设计模式
按书中分类
适应设计模式
- Iterator
- 好处:将遍历和实现分离,即while循环中的东西不依赖于BookShelf的实现
- Adapter
- 目的:让新的接口对原来的方法能够在不改的前提下对之前的进行兼容
- 使用 继承和委托的区别
- 委托需要注入,然后再调用
交给子类
- Template
- 这个模式挺重要的,后面很多模式会大量用到
- 父类定义整个处理流程,子类实现具体步骤
- Factory
- 不用自己手动来new实例,而是通过工厂来创建实例,实现和类名之间的耦合
- 工厂模式很多地方用到了,比如线程池。
- Singleton
- 这个是面试问的最多的,很多人熟悉,但是很多人不明白各种单例
- 懒汉,饿汉,双重校验锁。这些你都会吗? 双重校验锁再加volatile关键字呢?
- Singleton 这个模式很重要,以后工作也会大量用到。面试更不用说
- Prototype
- 根据现有的实例 来生成新的实例
- 除了使用 new 和 使用 反射的 newInstance 我们还有使用 clone的方法生成实例
- 因为返回的是MessageBox和UnderlinePen 是Product的实现,所以在create方法的返回可以使用 Product来接收这两个方法的createClone方法的返回值
组装复杂的实例
- Builder
- 和Tamplate类似,但是Builder的子类调用顺序在Director中,Tamplate在父类中
- Builder 感觉就是 委托+Tamplate
- Abstract Factory
- 优缺点:
- 优点:易于增加具体工厂
- 缺点:难以增加新的零件(因为已经全部写死在抽象父类,要改之前所有的具体工厂都要改)
- 在Item中,他的子类 Link 和 Tray 都是他的子类,所以在Page和Tray增加的时候中直接传入Item就行了,不需要知道具体是Link还是Tray
- 优缺点:
分开考虑
- Bridge
- 类的层次结构的两个作用
- 增加新的功能
- 增加新的实现
- Bridge 使用 委托+Tamplate
- 将类的功能层次和类的实现层次连接起来
- 类的层次结构的两个作用
- Strategy
- 将整个算法替换,能替换的基础还是都实现自Strategy接口。而且二者所有的都相同,只是函数实现方式不同
- 这里面的例子也有使用Singleton模式
- 也是用了委托
一致性
- Composite
- 能够使容器和内容具有一致性
- 不必知道传过来的是谁,只要知道他的父类就行
- Decorator
- 装饰边框与被装饰物具有一致性.因为装饰类(Border类和及其子类SideBorder,FullBorder)和被装饰类(StringDispla)都是Display的子类。所以说他们具有一致性
- 我们通过添加修饰类 SideDisplay 和FullDisplay 来增加对象的功能。
- 缺点:会增加很多功能类似的小类
访问数据结构
- Visitor
- 目的:为了将处理从数据结构中分离出来。保存数据结构和以数据结构为基础进行处理是两种不同的东西
- 、Visitor模式提高了 Directory 和 FIle 类作为组件的独立性
- Chain Of Responsibility
- 推卸责任,我处理不了的任务我转交给下一个,直到解决完或者任务到最后无法解决返回错误
- 目的:我不知道谁处理,谁分担什么责任。从而达到提高其作为组件的独立性
- 如果明确知道谁处理,就可以不用这个,直接交给其处理就行。因为这中模式花时间在转交任务
简单化
- Facade
- 目的:将各个类,业务之间的联系在内部实现,从而达到 程序与外部之间的关系弱化,更容易使包(类的集合)作为组件被复用
- 好处:维护方便
- 用在哪:当我们做大型项目的时候,往往有很多包,很多类。如果我们用这个来代替自己脑袋里所知道的调用规则,顺序,那么我们可以省多少时间去想他们的调用关系。
- Mediator
- Mediator 不让互相关联的对象之间进行任何通信,而是交给中介者来进行
- 好处:降低了各个类之间的耦合
- 缺点: 随着类的增加,中介者必然会膨胀的非常大而且复杂,难以维护
管理状态
- Observer(订阅发布、回调)
- 当观察的对象的状态发生变化时,会通知给观察者。
- 适用于:根据对象的状态进行相应处理的场景
- 目的:使类称为可复用的组件。在Observer模式中,带状态的RandomNumberGenerator 和接收状态变化的DigitObserver 和 GraphGenerator。链接这两个的使他们的接口 Observer 和 Generator
- 一方面,RandomNumberGenerator 类并不知道,也无须在意正在观察自己的是DigitObserver 还是 GraphObserver。他只需要知道 这些都实现了Observer接口就行,因为他们都会实现update方法
- 另一方面,DigitObserver 也无需在意自己观察的究竟是 RandomNumberGenerator类 还是其他。他只需要知道 这些类是 NumberGenerator 类的子类就行,并且有getNumber方法
- 注意:Observer的注册顺序会影响通知顺序,如果先添加 DigitObserver ,那么就先通知他,反过来亦然
- Memento
- 事先保存状态的相关信息,必要时可以恢复
- 注意控制访问实例内部结构的权限。
- 比如在这个例子中,我们无法从 gamer包外部来改变 Memento类中的信息,(注意是改变,不是访问,因为get方法还是public,外部是可以访问的)
- 我们在Main中也无法通过 new 的方法直接 new 出 Memento,但是Gamer可以访问Memento,也可以new 出实例
- State
- 使用类表示状态,
- 好处:不用每次都进行判断,只需要最开始的地方判断即可。然后后面的调用逻辑都很清晰,如果修改逻辑 ,基本上也不需要在调用的类中修改,只需要对状态类进行修改即可
避免浪费
- Flyweight
- 共享变量,同一个变量可以被多处使用,从而达到减少内存的作用
- 缺点:如果共享的变量修改了,那么引用这个变量的很多地方也会受影响
- Proxy
- 让别人(代理人)替自己做事,代理人毕竟是代理人,总会有他处理不了的是,如果代理人完不成事情,还会交给原来的人做
- 好处:提升处理速度。可以让那些加载耗时的等用到才被加载,简单的可以直接交给代理来做(比如代码中的heavyJob,这个完成需要5秒钟,只有用到这个的时候才会被加载,如果我们仅仅只是获取name或者是设置他的name,我们甚至并没有生成Printer的实例)
- 代理人(ProxyPrinter)知道Printer,但是Printer并不知道ProxyPrinter,所以他并不知道自己是被直接new的还是被ProxyPrinter创建的
用类来表现
- Command
- 一个类在进行工作时会调用自己或是其他类的方法,虽然调用结果会反映在对象的状态中,但并不会留下工作的历史记录。如果我们有一个类,用来表示"请进行这项工作"的"命令"就会方便很多
- Interpreter
- 解释器:我们用Java语言实现一个可以执行自己定义的"语言",让Java语言负责翻译。这样当需要解决的问题发生变化时,不需要修改Java语言程序,只需要修改我们需要翻译的那个语言的程序
- 例子中使用的是 EBNF,是BNF的一个变种。BNF——巴克斯范式还是挺需要研究的
访问数据结构
重要的设计模式(可以说是必须要会的)
- Singleton,不用说
- Factory
- Adapter 委托基本后面都要用到
- Proxy
- Tamplate 基础,后面很多模式都会用到这个模式
- Observer
- Chain Of Responsibility
- Mediator
- Flyeight
对比各个设计模式
使用了Tamplate模式的设计模式
- Factory
- Abstract Factory
- Bridge
- Decorator
- Chain Of Responsibility
使用了委托的设计模式(继承是强关联,委托是弱关联)
继承是子类和父类具有一致性,委托是委托类和被委托类具有一致性
- Adapter
- Bridge
- Strategy
- Decorator
- Visitor
- Mediator
笔记
创建实例的方法
- new 关键字(这个用的最多)
- Class.forname().newInstance().这个也常见
- clone 这个不常见,但是也很有用在prototype中
最后
以上就是顺利早晨为你收集整理的为什么要学习设计模式?看完这篇你就懂了!为啥要看学习前应当注意面向对象怎么学各个设计模式重要的设计模式(可以说是必须要会的)对比各个设计模式笔记的全部内容,希望文章能够帮你解决为什么要学习设计模式?看完这篇你就懂了!为啥要看学习前应当注意面向对象怎么学各个设计模式重要的设计模式(可以说是必须要会的)对比各个设计模式笔记所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复