个人简单概括总结
单例模式
定义:确保某个类只有一个实例,提供全局访问点。
特点:1、只有一个实例。 2、自我实例化。 3、提供全局访问点。
只有一个实例:通过私有构造函数来保证类外部不能对类进行实例化
提供全局的访问点:创建一个返回该类对象的静态方法
场景:
- 创建对象消耗的资源过多,如访问IO等资源;
- 需要共享数据或共享访问点,例如页面上的计数器,交易所的核心交易引擎。
- 生成唯一序列号;
- 定义大量静态方法的环境
优点:
- 提供对唯一实例的受控访问;。
- 减少内存开销
- 避免对共享资源的多重占用。
缺点:
- 不适用变化的对象,
- 没有抽象层,扩展困难。
实现:
非线程安全:1、私有构造函数 2 私用静态实例 3 创建返回该类对象的静态方法 4 私用实例为空创建并返回。
线程安全实现: 1 1 创建私有变量object ,对实例的null判断以及new的部分使用lock对object 加锁
双重验证的线程安全实现:加同步锁时,前后两次判断实例是否存在
不用锁的线程安全实现:使用静态私有,只读实例,在类第一次被调用创建实例
完全延迟加载实现:创建私有内部类的静态,只读实例,静态公共方法调用时返回该实例。
使用.NET4的Lazy<T>类型:.NET4或以上支持Lazy<T>来实现延迟加载
注意:
- 线程安全实现:每次上锁开销较大
- 双重锁定的作用:第一个判断null是减少进入锁的线程数;第二个判断null是防止进入锁的类重复创建实例;
- 不用锁的线程安全实现:被创建的时机不明,任何对类调用都会创建
扩展:
- 不同线程同时上锁,会出现线程等待,死锁。尽量选择类中的私有变量
- 不要将数据库连接做成单例
- 当类包含静态字段,没有定义静态的构造函数,该类会被标记为beforefieldinit,发生在任何时候任何字段被引用之前。
JAVA总结:
实现:
非线程安全:1、私有构造函数 2 私用静态实例 3 创建返回该类对象的静态方法 4 私用实例为空创建并返回。
线程安全实现: 1 对实例的null判断以及new的部分使用synchronized对class 加锁,对实例使用volatile关键字限制
双重验证的线程安全实现:加同步锁时,前后两次判断实例是否存在,
不用锁的线程安全实现:使用静态私有实例,在类第一次被调用创建实例
完全延迟加载实现:创建私有内部类的静态,只读实例,静态公共方法调用时返回该实例。
枚举写法:线程安全和防止反射强行调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。
注意:
volatile关键字:保证对所有线程的可见性,禁止对其进行指令重排序。
线程同步:在一个时刻只有一个线程能得到同步锁,当第一个线程加上锁以后,第二个线程只能等待。
懒汉式是典型的时间换空间,就是每次获取实例都会进行判断,看是否需要创建实例,
饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例
类级内部类相当于其外部类的成员,只有在第一次被使用的时候才被会装载。
JMM定义了线程和主内存之间的抽象关系:共享变量存储在主内存,每个线程都有本地内存保存了主内存的副本拷贝,线程对变量的操作都在工作内存中进行,而不能直接读写主内存中的变量。
可见性。声明为volatile后,:1.JMM把本地内存的变量刷新到主内存;2.其他线程的缓存无效.
简单工厂模式
定义:创建工厂类创建其它类的实例。
优点:1、实现创建和使用分离;
2 、只需知道创建类对应的参数
缺点:1、 扩展困难,添加新产品要修改工厂
2 、集中了所有产品创建逻辑。
场景:创建的对象较少;客户端只需知道传入工厂类的参数。
.NET中实现:System.Text.Encoding类的GetEncoding方法
JAVA总结:又叫做静态工厂方法(Static Factory Method)模式。
工厂方法模式
定义:定义创建对象的接口,将实际创建工作推迟到子类。
解决问题:简单工厂模式,类的变化修改工厂类。
工厂方法解决单个对象的需求变化;抽象工厂解决“系列对象”的需求变化;建造者解决“对象部分”的需求变化;
角色:
- 抽象具体工厂角色: 创建产品。
- 抽象具体产品(Product):由具体工厂类创建,它们之间有一一对应的关系。
优点: 1、 只需知道工厂,无须关心创建过程。
2 、增加新的产品,添加具体产品类和对应的实现工厂。
缺点:每增加产品,都要增加具体类和对象实现工厂,增加了系统复杂。
场景: 1、客户端不需要具体产品类的类名,只需知道所对应的工厂
2、抽象工厂类通过其子类来指定创建产品类,提高可扩展性。
.NET中实现:Asp.net中,处理程序对象是具体用来处理请求,请求*.aspx时,映射到System.Web.UI.PageHandlerFactory类上进行处理,*.ashx映射到System.Web.UI.SimpleHandlerFactory类中(都继承IHttpHandlerFactory)
JAVA总结:是创建模式,又叫虚拟构造子(Virtual Constructor)模式或多态性工厂(Polymorphic Factory)模式。
抽象工厂模式
定义:为创建一组相关对象或相互依赖对象提供接口,不指定具体类
角色:
- 抽象具体工产类:声明和创建同一产品族对象。
- 抽象具体产品类:声明同一类产品
优点:1.它分离了具体的类
2.它使得易于交换产品系列
3.它有利于产品的一致性
缺点:难以支持新种类的产品
场景
- 不依赖于产品类创建,组合和表达的细节。
- 有多个系列产品,只消费某一系列产品
- 提供一个产品类的库,以同样的接口出现,从而使客户端不依赖于实现。
.NET中实现:System.Data.Common.DbProviderFactory,提供了创建数据库连接时所需要的对象集合的接口,实际创建的工作在其子类工厂中进行,微软使用的是SQL Server数据库。
其实现要点有:提供多个产品的抽象接口:提供一系列对象的接口。
每个具体工厂创建一个产品族中的多个产品对象,多个具体工厂就可以创建多个产品族中的多个对象了。
抽象工厂模式与工厂方法模式区别
- 工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则是针对的多个产品等级结构。
JAVA总结:
产品等级结构:产品等级结构即产品的继承结构
产品族,是指位于不同产品等级结构中,功能相关联的产品组成的家族。
建造者模式
定义:构建与表示分离,同样的构建不同的表示。
角色:
抽象建造者(Builder)角色:规范各个部件的建造。有建造方法和返还结构方法。零件数目与建造方法的数目相符。
具体建造者(ConcreteBuilder)角色:1.实现接口构造部件。2.提供产品的实例。
导演者(Director)角色:调用具体建造者角色创建产品对象
产品(Product)角色: ConcreteBuilder创建该产品的内部表示并定义它的装配过程
场景:建造顺序稳定,内部的构建变化。
优点::1.封装性。客户端不必知道产品内部组成细节。
2. 建造者独立,易扩展。 3.对构造过程更加精细控制
缺点: 1 产品必须有共同点,范围有限制。2 内部变化复杂,会有很多建造类。
与工厂模式的区别:更关注于零件装配的顺序,抽象工厂解决“系列产品”的需求变化,建造者解决 “产品部分” 的需求变化。
.NET中实现:System.Text.StringBuilder,ToString()返回具体产品,Append创建产品的组件
JAVA总结:
又名生成器模式,是对象构建模式。
产品的内部表象:产品不同的组成成分
对象性质的建造:零件的建造过程往往被“外部化”到建造者的对里,建造者对象返还给客户端的是零件都建造完毕的产品对象。
建造模式都存在两部分,一个部分是部件构造和产品装配,另一个部分是整体构建的算法。
使用建造模式构建复杂对象:可去掉抽象建造和导演。
应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。 2、JAVA 中的 StringBuilder。
解决:将变与不变分离开。
原型模式
定义:用原型实例指定创建对象的种类,通过复制原型创建新的对象。
优点:
- 隐藏创建新实例的复杂性
- 简化创建结构。
- 可以使用深克隆保持对象的状态。
原型模式的缺点有: 1、类必须配备克隆方法,需要对功能通盘考虑
2、 深克隆时需要编写较为复杂
场景: 1 创建新对象成本较大
2 系统要保存对象的状态,而对象的状态很小
3 避免使用分层次的工厂类,对象只有很少的组合状态
.NET中原型模式的实现:实现ICloneable的Clone()
1 使用MemberwiseClone实现浅拷贝,使用序列化实现深拷贝
浅复制:复制的值类型的值和引用类型的引用,因为2个对象指向同一个内存地址,那么任何一个修改操作都会产生改变。
深复制:复制的值类型的值和引用类型指向新对象。
JAVA总结:
表现形式:(1)简单形式、(2)登记形式
角色:抽象原型角色:定义接口;具体原型角色:被复制的对象。实现复制接口
登记形式:多出原型管理器角色,作用是:创建并记录对象。
简单形式:对象数目较少比较固定; 登记形式:对象数目不固定的话
Cloneable接口:通知Java虚拟机安全使用clone()。否则调用clone()方法会抛出CloneNotSupportedException异常。
条件:
- x.clone()!=x,不是同一个对象。
- x.clone().getClass() == x.getClass() ,类型一样
- equals()定义恰当,x.clone().equals(x)。
浅复制是java.lang.Object的clone(),深复制实现java.io.Serializable接口
适配器模式
定义:把接口变换期待的另一种接口。
类的适配器模式:实现期待类的接口,继承现有类
对象的适配器模式:实现期待类的接口,持有现有类
类的适配器模式优点:1、不修改原有代码复用现有类
2、 重新定义被适配的类的部分行为
3 、仅仅引入对象,不需要额外的字段。
类的适配器模式缺点:1、用具体类匹配,匹配类及它的子类,就不能胜任。不能调用对应子类的方法。
2、采用 “多继承”,带来高耦合。
对象的适配器模式优点:1、不修改原有代码复用现有类
2、采用 “对象组合”,更符合松耦合。
缺点:重定义被适配的类行为困难,生成子类并引用
JAVA总结:
角色:目标角色:期待得到的接口。源角色:现在需要适配的接口。适配器(Adaper)角色:把源接口转换成目标接口。
类适配器和对象适配器的权衡
- 类适配器使用对象继承,是静态的;而对象适配器使用对象组合,是动态。
- 对于类适配器,适配器继承源类,静态的关系,不能处理子类
- 对于对象适配器,把源类和它的子类都适配到目标接口。
- 对于类适配器,重定义源类的部分行为
- 对于对象适配器,重定义源类的行为困难,增加新的行为方便
- 对于类适配器,仅仅引入了对象,并不需要额外的引用
- 对于对象适配器,需要额外的引用来间接得到Adaptee。
- 建议尽量使用对象适配器的实现方式,多用合成/聚合、少用继承。
优点
- 更好的复用性:接口不符合系统的需要。通过适配器模式到复用。
- 更好的扩展性:可以调用自己开发的功能,从而扩展功能。
适配器模式的缺点:过多的使用适配器,会让系统非常零乱,不易整体进行把握。
缺省适配模式为一个接口提供缺省实现,这样子类型可以从这个缺省实现进行扩展,而不必从原有接口进行扩展。
适配器模式的是要改变源的接口,以便于目标接口相容。缺省适配建立一个不平庸的适配器类而提供的一种平庸实现。
实现:制造抽象类,给出所有方法的平庸的具体实现。继承的子类就不必实现所有的方法。
源代码
桥接模式
定义:抽象部分与实现部分脱耦,独立变化。
自我理解实现:抽象部分不变,实现部分用抽象类定义,持有抽象类引用
优点:
把抽象接口与实现解耦。
抽象和实现独立扩展,不会影响到对方。
对客户透明,隐藏了具体实现细节。
缺点: 增加了系统的复杂度
场景:
- 在抽象和实现添加更多灵活性,避免建立静态的联系。
- 实现的改变不影响客户端.
- 需要跨越多个平台的图形和窗口系统上。
- 一个类存在两个独立变化的维度,且都需要扩展。
C#实现:三层架构,在BLL层引用了DAL层。数据操作的实现在不改变客户端情况下更换
JAVA总结:
是结构模式。又称为柄体模式或接口模式。
- 抽象化:从众多的事物中抽取出共同的、本质性的特征,而舍弃其非本质的特征,就是抽象化。
- 实现化:抽象化给出的具体实现,就是实现化。
- 脱耦:耦合是两个实体的行为的某种强关联。将强关联去掉,就是耦合的解脱,或称脱耦。
- 强关联:在编译时期确定无法在运行时改变的关联;
- 弱关联:在运行时期改变的关联。
一组对象具有相同的特征,通过共同的类描述。一些类具有相同的特征,通过抽象类来描述。
继承关系是强关联,而聚合关系是弱关联。
桥梁模式中的脱耦,使用聚合关系而不是继承关系,使可以独立地变化。
角色有:
- 抽象化角色:定义接口,保存对抽象实现化对象的引用。
- 修正抽象化角色:实现,扩展,改变。
- 实现化角色:定义接口。实现化只给出底层操作,而抽象化只给出更高一层的操作。
- 具体实现化角色:实现。
抽象化像是水杯的手柄,实现是水杯的杯身。手柄控制杯身,别名“柄体”的来源。
向不同的实现化对象委派,达到动态转换功能的目的。
应用:JDBC提供通用的界面。应用系统动态地选择合适的驱动器,通过驱动器向数据库引擎发出指令。这个过程就是将抽象角色的行为委派给实现角色的过程。
对于应用程序而言,选用不同的驱动,就可以操作不同数据库,而无需更改应用程序。
装饰者模式
定义:不必改变原类文件和使用继承,动态地扩展功能。通过创建包装对象包裹真实对象。
自我理解实现:装饰角色:持有构件对象的实例, 执行原方法并附加责任。
角色:
- 抽象具体构件角色:定义规范接口。|定义接收附加责任的类。
- 抽象具体装饰角色:持有构件对象的实例,定义与构件接口一致的接口。 |负责给构件对象 ”贴上“附加的责任。
优点:
- 装饰者和继承都是扩展对象的功能,但装饰者更灵活
- 用不同的排列组合,创造不同行为的组合
- 有很好地可扩展性
缺点:更多的对象使得查错变困难,特别是看上去很像。
场景
- 扩展功能。
- 动态地增加和撤销。
- 增加由基本功能的排列组合而产生的功能
C#实现:stream抽象构件,对MemoryStream,BufferedStream扩展缓冲的功能,GZipStream 添加压缩功能
个人总结:装饰类实现构件接口,既装饰构件又装饰含有装饰的构件。(不继承父类,只实现接口)
JAVA总结:
又名包装(Wrapper)模式。
以对客户透明的方式动态地附加责任。客户端不觉得对象在装饰前和装饰后有不同。不创造子类,将对象的功能扩展。
简化:只有一个构件或装饰,去掉抽象(接口)。
透明性的要求:不声明装饰,声明构件t类型。
半透明的装饰模式:允许装饰模式改变接口,增加新的方法。声明装饰。
JAVA实现:
- 抽象构件:InputStream扮演。提供统一的接口。
- 具体构件:ByteArrayInputStream、FileInputStream、PipedInputStream、StringBufferInputStream等类扮演。实现接口。
- 抽象装饰:FilterInputStream扮演。实现接口
- 具体装饰:BufferedInputStream、DataInputStream以及不常用类LineNumberInputStream、PushbackInputStream。
装饰模式和适配器模式都是“包装模式(Wrapper Pattern)”,通过封装对象达到设计目的,形态有区别。
理想的装饰模式进行功能增强,要求接口一致
适配器模式改变源对象的接口,和目标接口符合。
InputStream类型中的装饰模式是半透明的
组合模式
定义:将对象组合成树形结构表现”部分-整体“的层次结构,以一致的方式处理单个对象以及对象组合。
实现关键——简单对象和复合对象实现相同的接口。
安全式的组合模式:树枝结构定义管理子对象。
透明式的组合模式:抽象构件角色定义管理子对象。
角色:
- 抽象构件角色:定义公共接口,可以管理子对象(在透明式)。
- 树叶构件(Leaf)角色:没有下级子对象的对象,定义参加组合的原始对象的行为
- 树枝构件(Composite)角色:有下级子对象的对象,(安全式)给出所有管理子对象的方法实现,如Add、Remove等。
优点:
- 一致地处理对象和对象容器。
- 将代码与容器结构“解耦。
- 更容易加入新构件。
缺点:更多时间理清类的层次关系。
注意问题: 1 遍历子构件多次,在父构件作缓存。2 不调用树叶类中的方法,借用父类多态性调用。
场景:1 表示整体或部分的层次结构。2 忽略组合对象与单个对象的不同。
java总结:
两种实现方法的选择
安全性合成模式:不会有误操作可能,能访问的方法都是被支持的。
透明性合成模式:不需要区分是“树枝对象”还是“树叶对象”。
合成模式会更看重透明性,目的是:以统一的方式来操作子对象和对象组合。
安全性的实现,区分树枝对象还是树叶对象。需要进行类型转换,却发现类型信息丢失了,只好强行转换,必然是不够安全的。
建议多采用透明性的实现。
外观模式
定义:提供了统一的接口,访问子系统的一群接口
实现核心:由外观类保存各个子系统的引用
区别:适配器是将接口转换为不同接口,而外观模式是提供一个统一的接口来简化接口。
角色:
- 门面角色:将从客户端发来的请求委派带相应的子系统中去。
- 子系统角色:是一个类的集合。对于子系统,门面仅仅是另外一个客户端。
优点:1.屏蔽了子系统组件,简化接口。
2.实现了子系统与客户的松耦合关系,松耦合使得子系统的组件变化不会影响到它的客户。
3.更好的划分访问层次
缺点:增加新的子系统可能需要修改外观类或客户端的源代码,这样就违背了”开——闭原则“(不过这点也是不可避免)。
场景:
- 为复杂的子系统提供简单的接口
- 提供子系统的独立性
- 在层次化结构中,可以使用外观模式定义每一层的入口。
应用:三层架构就是这样的一个例子。
java总结:
一个系统可以有几个门面类:。对每一个子系统只有一个门面类。是单例类。
为子系统增加新行为:门面模式的用意是为子系统提供集中化和简化的沟通管道,而不能向子系统加入新的行为。
应用:使用Servlet,在web.xml配置外,继承HttpServlet,重写doGet与doPost方法(或重写service)。
doGet与doPost方法有两个参数,参数类型是接口HttpServletRequest与接口HttpServletResponse
StandardWrapperValue的invoke方法并没有直接将Request对象与Response对象传递给ApplicationFilterChain类的doFilter方法,传递的是RequestFacade与ResponseFacade对象
好处:Request使用Facade类,将与内部组件交互使用的方法屏蔽掉,只提供给外部程序感兴趣的方法。
享元模式:
定义:运用共享技术支持大量细粒度的对象
场景:需要生成大量实例,除了几个参数基本相同。
内部状态:内部不会随环境改变而改变的共享部分
外部状态:随环境改变而改变的,不可以共享部分。
角色:
- 抽象具体享元角色:定义接口。定义内部状态,需要外部状态的方法以参数形式传入。
- 享元工厂角色:创建和管理享元,调用享元对象时,检查已存在,提供这个对象,不存在创建一个。
场景:
- 1 有大量对象;2 这些对象耗费大量内存;
- 状态大部分可以被外部化
- 按照内部状态分成很多组,当把外部对象从对象中剔除时,每一个组都可以仅用一个对象代替
- 系统不依赖这些对象的身份,
享元模式需要维护记录已有享元的表,需要耗费资源,
优点:降低对象数量,降低内存压力。
缺点:1 需要将状态外部化,使逻辑更复杂。2 读取外部状态使得运行时间变长。
应用:在.NET类库中,string类的驻留池实现就使用了享元模式
JAVA总结
String是final类型,一旦创建不可改变。确保一个字符串常量在常量池中只有一个拷贝。
单纯享元模式:所有的享元对象都是可以共享的。
复合享元模式:将单纯享元使用合成模式复合,形成复合享元对象。本身不能共享,分解成单纯享元对象可以共享。
代理模式:
定义:给对象提供代理,并由代理对象控制对原对象的引用
目的或场景:
- 远程(Remote)代理:为不同地址空间的对象提供代理对象。例子—调用Web服务或WCF服务。
- 虚拟(Virtual)代理:创建消耗大的对象,在需要时才创建。
- Copy-on-Write代理:把复制拖延到需要时采取行动。
- 保护(Protect or Access)代理:控制对象的访问,提供不同级别的使用权限。
- 防火墙(Firewall)代理:不让恶意用户接近。
- 智能引用(Smart Reference)代理:提供额外操作,比如记录调用的次数记来等。
- Cache代理:为操作结果提供临时的存储空间
角色:
- 抽象主题角色(Person):声明公共接口。
- 代理主题角色:含有对真实主题的引用;需要时创建真实主题对象;执行额外操作。
- 真实主题角色(RealBuyPerson):定义。
优点:
- 将目标对象隔离,降低耦合度
- 对目标对象保护,添加额外操作
缺点
- 处理速度变慢
- 增加复杂度
Java总结:
静态代理是指预先确定了代理与被代理者的关系,
动态代理是代理与被代理人的关系是动态确定的
代理模式的关键点是:代理对象是对目标对象的扩展,并会调用目标对象
静态代理:代理对象与代理对象实现相同的接口或者父类
动态代理,是利用JDK的API,动态的内存中构建代理对象(需要指定代理对象的(目标对象实现的)接口类型),也叫jdk代理
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
AOP目标是通过隔离切面耦合来增加程序的模块化。
切面耦合,我的理解各个模块相同业务代码
模板方法:
定义:抽象类定义算法骨架,将步骤延迟到子类实现
角色:
- 抽象模板:1 定义抽象操作,让子类实现,称为基本操作。 2 实现模板方法,给出顶级逻辑的骨架
- 具体模板:实现父类定义抽象方法。
应用:开发自定义的Web或WinForm控件时,只需重写控件的部分方法。
优点: 1.实现代码复用 2.灵活应对子步骤变化
缺点:引入抽象类,实现过多的话,花更多时间理清类关系。
Java总结:
代表具体逻辑步骤的方法称做基本方法;
将基本方法汇总的方法叫做模板方法‘’
模板方法的行为称为顶级行为,其逻辑称为顶级逻辑。
模板方法:抽象类可以有多个模板方法。
基本方法:
- 抽象方法:抽象类声明,子类实现。abstract标示。
- 具体方法:具体方法抽象类声明并实现,而子类并不实现或置换。
- 钩子方法:子方法由抽象类声明并实现,而子类会加以扩展。
默认钩子方法:给出空实现作为默认实现。
命名规则:以do开始
Servlet的应用:web.xml配置,继承HttpServlet的抽象类。提供service()方法,调用七个do方法,对客户端调用的响应。由具体子类提供。
自己实现HttpServlet类,一般置换父类的:doGet()和doPost()。
HttpServlet担任抽象模板:模板方法:service()方法。基本方法:doPost()、doGet()
TestServlet担任具体模板:实现doGet()和doPost()。
命令模式:
定义:行为抽象为对象,实现请求者和实现者的松耦合(百度百科)
角色:
- 客户:发出命令并确定其接受者。
- 命令:声明命令的抽象接口
- 具体命令:定义接受者和行为的弱耦合,调用接受者。
- 请求者:调用命令对象执行命令。
- 接受者:具体行为的执行。
应用:MVC模式中,有种叫Front Controller的模式,它分为Handler和Command树,Handler处理公共的逻辑,接收请求根据参数选择命令对象,控制权传递到Command对象。
场景:
1需要命令的撤销和恢复 2 需要在不同的时间请求、将请求排队
3数据更新到日志以便恢复 4作为callBack(回调)的代替
优点:
1 很容易添加新的命令。 2可以设计命令队列来实现对请求的撤销和恢复。
3 容易将命令写入日志。 4 把命令对象聚合为合成命令。合成命令式合成模式的应用。
缺点:
- 导致有过多的具体命令类。这会使得命令模式在这样的系统里变得不实际。
JAVA总结:
属于行为模式。又称为行动(Action)模式或交易(Transaction)模式。
宏命令简单点是包含多个命令的命令,是命令的组合。
迭代器模式:
定义:顺序访问聚合对象元素,无需暴露内部表示。
角色组成:
- 抽象具体迭代器角色(Iterator):定义访问和遍历元素的接口,记录当前位置
- 抽象具体聚合角色(Concrete Aggregate):获得迭代器角色。
场景或者优点:
- 访问聚合对象的内容无需暴露内部表示。
- 支持对聚合对象的多种遍历。
- 为不同的聚合结构提供统一的接口。
缺陷:
- 遍历的同时更改迭代器所在的集合结构会导致出现异常。
应用:C#中IEnumerator接口扮迭代器,IEnumberable接口则扮演聚集,有GetEnumerator()方法。
JAVA总结:
聚集和JAVA聚集:多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。数组就是最基本的聚集
JAVA聚集对象是实现了共同的java.util.Collection接口的对象,从1.2版开始,提供了很多种聚集,包括Vector、ArrayList、HashSet、HashMap、Hashtable等。
宽接口:聚集的接口提供修改聚集元素的方法。
白箱聚集:聚集对象为所有对象提供宽接口
游标迭代子:迭代子从外部控制聚集元素迭代过程。
外禀迭代子:迭代子是在聚集结构之外
意义: 1 支持对聚集对象的多种遍历。2 为不同的聚集结构提供统一的接口。
窄接口:聚集的接口没有提供修改元素的方法
黑箱聚集:为迭代子对象提供宽接口,为其他对象提供窄接口
黑箱实现方案:实现双重接口将迭代子类设计成聚集类的内部成员类
内禀迭代子(Intrinsic Iterator):迭代子是在聚集的结构之内定义
黑箱效果:迭代子可以访问聚集的元素,外界不能访问。
主动(外部)迭代子:客户端控制迭代元素,调用迭代子的next()等迭代方法。
被动(内部)迭代子:迭代子控制迭代元素。客户端需要传入操作,迭代元素时执行,类似回调。
静态迭代子:聚集对象创建快照。客户端修改原聚集内容,迭代子对象不会反映新变化。
静态迭代子的好处是安全和简易,短处是对时间和内存的消耗。
动态迭代子:创建迭代子,保持着对聚集元素的引用,修改原聚集会在迭代子对象上反映。
Fail Fast:算法开始之后,运算环境发生变化,使得无法进行必需的调整时,发出故障信号。
Fail Fast在JAVA聚集中的使用:JAVA语言以接口java.util.Iterator支持迭代子模式,Collection接口要求提供iterator()方法,此方法在调用时返还一个Iterator类型的对象。而作为Collection接口的子类型,AbstractList类的内部成员类Itr便是实现Iterator接口的类。
Itr类的方法checkForComodification()会检查聚集的内容是否刚刚被外界直接修改过(不是通过迭代子提供的方法修改的)。如果在迭代开始后,聚集的内容被外界绕过迭代子对象而直接修改的话,这个方法会立即抛出ConcurrentModificationException()异常。
优点:(1)简化接口。迭代子具备遍历接口,聚集不必具备遍历接口。(2)聚集对象可以有几个迭代在进行。
(3)支持对聚集对象的多种遍历
观察者模式:
定义:定义一对多的依赖关系,观察者监听主题对象,主题状态变化通知观察者更新行为。
角色:
- 抽象具体主题角色(Subject):持有观察者列表,提供增加和删除。状态改变时给观察者通知。
- 抽象具体观察者角色(Observer):得到主题通知更新自己
使用委托与事件可以简化观察者模式实现
适用场景:
- 一方面依赖另一方面,封装在独立的对象能独立改变和复用。
- 对象的改变需要改变其他对象,不知道多少对象待改变。
- 对象必须通知其他对象,不知道对象是谁。
优点: 1 实现表示层和逻辑层的分离,使得可以有不同的表示层(观察者)。
2 建立抽象的耦合,主体不知道具体的观察者,只是保存抽象列表。3 支持广播通信。会向所有观察者发出通知。
缺点: 1 通知所有观察者花费时间。 2 没有相应的机制使观察者知道怎样发生变化。
3 观察者有循环依赖,触发循环调用,导致系统崩溃。
Java总结:
是行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
推模型:主题向观察者推送信息,不管观察者是否需要,推送全部或部分数据。
拉模型:主题传递少量信息。观察者需要更多信息,主动到主题获取,相当于拉数据。一般实现把主题对象自传递。
推模型是假定主题知道观察者需要数据;而拉模型是主题对象不知道观察者需要数据。
推模型可能使得观察者难以复用。而拉模型不会造成这样的情况,传递主题对象本身,是能传递的最大数据集合,适应各种情况。
JAVA语言的java.util库,提供Observable以及Observer接口,对观察者模式的支持。
中介者模式:
定义:定义中介对象封装对象交互关系。
适用场景:1 一组对象进行复杂的通信。2 封装多个类的行为,不想生成太多的子类。
优点: 1 简化对象关系 2 各个对象独立易于复用。 3多对多变成一对多
缺点: 1中介者承担较多的责任,出现问题,受到重大影响。
2 新增加同事类时,修改中介者类
java 中介终结
又叫调停者模式
角色:1 抽象和具体调停者角色:定义同事到调停者的接口。知晓所有同事,协调交互关系。
2 抽象和同事类角色:定义调停者到同事的接口。同事只知道调停者。需要与同事通信,与调停者通信。
状态者模式:
内部状态改变时自动改变其行为
角色:
- 环境类类:维护状态类。
- 抽象状态类:定义行为约定。
- 具体状态类:实现行为。根据状态改变行为
状态者模式的应用场景:1 条件表达式过于复杂时 2行为取决于它的状态,运行时刻根据状态改变行为
状态者模式的主要优点是: 1 状态判断逻辑每个状态类里面,简化判断逻辑。2新状态添加新类来扩展。
状态者模式的主要缺点是:如果状态过多,会有非常多的状态类
JAVA总结:
状态和行为
状态,通常是属性;行为指的功能,具体是方法。
状态模式的功能是分离状态的行为,状态决定行为。行为在运行期根据状态的改变而改变。
行为的平行性:相互之间是不可替换的。
平等性是可替换性
环境和状态处理对象
环境是持有状态的对象,处理状态的委托给了状态类处理。
客户端只和环境交互。配置完毕,不需要和状态对交道了。
C#策略者模式:
定义:将不同算法封装到不同类,可以相互替换
角色:环境,抽象策略,具体策略
实现:环境持有抽象引用,抽象策略定义算法接口,具体策略实现算法
c#实现:LIst的排序功能,List<T>.Sort(IComparer<T>)
适用场景:1 动态选择算法 2 有大量行为,避免多重if
优缺点: 1 自由切换。2 易于扩展 3 避免多重条件选择语句
缺点:必须知道所有策略,自行决定使用哪一个。用ioc解决。
JAVA总结:
策略模式的重心:组织调度算法
算法的平等性:相同行为的不同实现
运行时策略的唯一性:一个时刻只能使用一个具体实现
公有的行为:公有行为在抽象类
责任链模式:
责任链模式,多个对象组成链子传递该请求,直到有对象处理它为止
角色:
- 抽象处理者角色(Handler):定义处理请求的接口。
- 具体处理者角色(ConcreteHandler):受到请求后,选择处理掉,或者传给下一个。
场景 1 审批需要多个对象 2存在多个if-else语句
优缺点 1降低了发送者和接收者耦合。2 把多个条件分散到各个处理类中。
缺点,如: 1 未在找到正确,所有的条件判定都要执行 2可能某个请求不被处理。
jAVA总结:
纯的责任链模式:处理者只能选择一个行为:一是承担责任 2 把责任推给下家
纯的责任链,请求必须被某个处理者接收;在不纯的责任链,请求可以不被对象接收。
Tomcat的Filter使用责任链模式,创建Filter要在web.xml做配置,还要实现javax.servlet.Filter接口。
Tomcat的容器设置也是责任链模式,从Engine到Host再到Context一直到Wrapper都是通过链传递请求。
ApplicationFilterChain类扮演抽象处理者,而具体处理者角色由各个Filter扮演。
Filter存放保存在ApplicationFilterChain类中的一个ApplicationFilterConfig对象的数组中。
当web应用首次启动ApplicationFilterConfig会自动实例化,从web.xml读取配置的Filter,然后装进该容器。
访问者C#:
封装的数据结构上的操作,操作和数据结构解耦。
角色:
- 抽象具体访问者:声明访问操作,接受节点对象参数。
- 抽象具体节点:声明接受操作,接受访问者对象参数。
- 结构对象:节点容器,包含多个不同类或接口的容器。
双重分派:节点调用访问者,将自己传入,访问者则将某算法针对此节点执行。
使用场景: 1 稳定的结构,变化的算法 2 一组类存在相似结构 3 一个对象存在不相干操作
优缺点:
1 添加操作容易,添加新类
2 有关操作集中到访问者对象
3 访问不同等级对象
缺点:
添加新元素类困难,要在访问者添加新操作。
java总结:
变量被声明时的类型叫做变量的静态类型(Static Type),明显类型(Apparent Type);
而变量所引用的对象的真实类型又叫做变量的实际类型(Actual Type)。
根据对象的类型而对方法进行的选择,就是分派(Dispatch)
静态分派(Static Dispatch)发生在编译时期,分派根据静态类型。比如方法重载就。
动态分派(Dynamic Dispatch)发生在运行时期,动态分派动态地置换掉某个方法。比如重写
方法所属的对象叫做方法的接收者,接收者与参数统称做方法的宗量。
单分派语言根据一个宗量的,多分派语言根据多个的宗量。
Java就是动态的单分派语言,考虑接收者的类型,又是静态的多分派语言,考虑类和参数
一个方法根据两个宗量的类型来决定执行不同的代码,这就是“双重分派”
在Java中可以通过两次方法调用来达到两次分派的目的。
动态单分派在Java语言中是在子类重写父类的方法时发生的。
C# 备忘录:
定义:对象之外保存它的内部状态,以便恢复
1 场景: 提供回滚:比如数据库事务
2 优缺点 1 恢复数据 2 备忘录保存备份 ,单一职责 3 缺点:维护多个备份消耗资源
- 发起人角色:创建和恢复备忘录数据。
- 备忘录角色:1 存储内部状态,恢复时提供状态。2保护内容不被发起人之外读取。
- 管理者(负责人)角色:1 保存备忘录对象。2不检查备忘录的内容。
JAVA总结:
又叫快照或Token模式,是行为模式。
两个接口: 1 窄接口:只允许把备忘录对象传给其他的对象。 2宽接口:允许读取所有的数据
备忘录角色对任何对象都提供宽接口,叫做“白箱实现”。
备忘录角色对发起人提供宽接口,其他对象提供窄接口。叫做“黑箱实现”。
在JAVA,实现双重接口的办法就是将备忘录角色类设计成发起人角色类的内部成员类。
在“自述历史”模式里面,发起人角色自己兼任负责人角色。
解释器模式
定义:给定一种语言,定义它的文法表示,并定义解释器使用该表示来解释该语言。