PS:本人技术很渣,理解浅显,望技术大牛看过之后能够批评指正。
1.工厂方法模式:
1.2 工厂方法模式
当我创建了1.1的工厂类,如果我新增加一个要生产的对象,势必要修改工厂的静态方法,那么很明显违背了开闭原则
那么,我可以这么做:将方法抽象出来,我创建哪个对象就new 哪个对象的工厂类就可以了,但是这种方法不适合构建对象种类特别多的时候,如果特别多则工厂类也多。
public interface AbstractFactory {
/**
* 生产 car子类
*
* @return
*/
Car build();
}
public class AudiCar extends Car {
}
public class AudiCarFactory implements AbstractFactory {
@Override
public Car build() {
return new AudiCar();
}
}
public class CarFactoryUse {
public static void main(String[] s) {
AbstractFactory factory = new AudiCarFactory();
factory.build();
}
}
1.3 抽象工厂模式
他的使用场景:就是系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
比如说,我要生产一组相关的产品,如电脑厂商 生产主板和键盘, 主板和键盘是相关的产品
于是乎我有一个接口 absFactory 里面有两个方法 buildMainBoard 和buildMouse ,
方法的返回值都是抽象类。
则华硕实现这个接口 我生产华硕的鼠标 ,生产华硕的主板
则雷蛇实现这个接口 我生产雷蛇的鼠标 ,生产雷蛇的主板
或者xxx实现这个接口 我生产苹果的鼠标,微星的主板
主板或者鼠标是相关的产品,而不同厂商的鼠标键盘是不同的,我这里可以自由操作,这个两个维度不好理解
----------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线------------------------------------------------------2.单例模式
单例模式来说,顾名思义:获得这个对象永远是一个实例。这里面最重要的一步就是,不允许外面的使用者创建实例,即初始化方式私有化。此处不考虑有状态的单例模式(类似于工厂模式的单例模式)
这里提一点,如何破坏单例呢?
clone ,如何解决这个问题呢?在序列化时定义了readResolve()这个方法。
单例模式共有两种:
1 饿汉式 :处理先new 出来占内存,其余的没什么特殊注意的
public class Singleton {
//在自己内部定义自己一个实例
//注意这是 private 只供内部调用
private static Singleton instance = new Singleton();
//如上面所述,将构造函数设置为私有
private Singleton(){
}
//静态工厂方法,提供了一个供外部访问得到对象的静态方法
public static Singleton getInstance() {
return instance;
}
}
2 懒汉式 :这里要注意线程安全问题,切记
有很多对于此模式的讨论:https://www.cnblogs.com/zhaoyan001/p/6365064.html
public class Singleton {
//和上面有什么不同?
private static Singleton instance = null;
//设置为私有的构造函数
private Singleton() {
}
//静态工厂方法
public static synchronized Singleton getInstance() {
//这个方法比上面有所改进
if (instance == null)
instance=new Singleton();
return instance;
}
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线------------------------------------------------------
3.建造模式
这个设计模式呢主要用于构建步骤繁杂的对象,这是和上面的工厂模式的主要区别
它有下面的模块:
1.抽象建造者,对建造者的约束,规定创建对象及对必要的成员初始化的步骤的步骤
2.建造者,具体的建造实现,实现或继承抽象建造者的构建规定,持有目标对象的引用
3.指导者,通过调用建造者的对象,封装构建对象的具体步骤,达到一个api即可获得成品OBJ的效果
就是指导者操作建造者来按照指定的步骤构建复杂实例
----------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线------------------------------------------------------
4.原型模式
这个模式呢相当于工厂模式简化之后对象不是new出来的,而是克隆出来的。
而克隆出来和new 出来哪个好就需要看需求了:
1.原型模式使用 clone 能够动态的抽取当前对象运行时的状态并且克隆到新的对象中,新
对象就可以在此基础上进行操作而不损坏原有对象;而 new 只能得到一个刚初始化的对象,
而在实际应用中,这往往是不够的。
2.这时对封装有大量数据的类进行 clone 要比再次连接数据库得到数据好的多
这里我们要讨论这种模式提到的克隆:
克隆就是克隆数据的意思,克隆主要分两种,本模式中要注意浅克隆引用的问题,看需求要不要共享
1.什么是浅克隆
克隆之后对象内部成员的引用是公用的
常见直接object的clone方法
2.什么是深克隆
克隆之后里面的所有成员都是新的,都不是共有的.
常见做法
1.递归克隆内部
2.序列化再读出来,不考虑readResolve方法
3.json序列化恢复
----------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线------------------------------------------------------
5.适配器模式
5.1 最简单的一种,把所有接口做空实现,那么我继承空实现就不需要实现所有接口了
5.2 另外一种,比如说我写了2个接口 ,实现的时候我发现有一个大牛已经对一个接口做了实现,然而接口名字不一样,然而为难的是,我这里不能改名字了,那怎么办,我可以使用组合继承的方式去实现它,用我的实现去调别人做好的实现。而这种适配的这一层,就叫做适配器。
这里要区别装饰着模式:装饰着强调对接口的修改,而适配性强调适配的特性
----------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线------------------------------------------------------
6.桥梁模式
这种设计模式就是说,实现和抽象可以单独变化。怎么说呢,就是说我可以组合多个接口来实现指定功能,而接口的实现是对我透明的。
那有什么使用场景呢?
当你的系统中有多个地方要使用到类似的行为,或者是多个类似行为的组合时,可以考虑使用桥梁模式来提高重用,并减少因为行为的差异而产生的子类。而这种就类似于装饰着了。
比如说我要实现发消息:
那么如果我增加一种发短信的方式:则
那么这里面我该如何抽取呢?
我们发现,发xxx这些端口细节是死的,无论只是发的渠道不同(Mobile SMS Email),而做的操作完全就是发出去,而发什么,发出去成功做什么这些是变化的。或者说send方法是公共的部分,应该剥离出来
那么我们的类图应该是:
那么我们总结一下这里面的角色:
1.抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。
2.修正抽象化(RefinedAbstraction)角色:对抽象化角色的修正,就是变化的部分,这里会增加接口,会增加功能,或拓展功能
3.实现化(Implementor)角色:不变的部分,要深刻理解不变的含义,这个角色给出实现化角色的接口
4.具体实现化(ConcreteImplementor)角色:这个角色给出实现化角色接口的具体实现。
这样中间的组合关系就相当于是桥梁,把两边链接有隔离开来,增加发送方式则不会改变前面的逻辑,而前面的发送逻辑改变也不会改变后面发送功能上的实现。
这种设计模式,就是抽离不变和万变。
----------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线------------------------------------------------------
7.组合模式
组合(Composite)模式的其它翻译名称也很多,比如合成模式、树模式等等。在《设计
模式》一书中给出的定义是:将对象以树形结构组织起来,以达成“部分-整体”的层次结构,
使得客户端对单个对象和组合对象的使用具有一致性。
比如说文件系统,文件夹和文件,典型结构如下
在这个模式中,有一个根节点,树叶节点和树枝节点,属于继承关系。
这里面根节点定义的是通用操作,这里涉及到安全性和透明性的问题,我们组合模式就是对根节点下的每一个元素的操作都一视同仁,对调用者来说都是透明的,那么我把管理子元素的接口写在根节点上,则树叶节点也有了这个接口,这样安全性就不好了,那么我把管理子元素的接口写在树枝上,那么透明性就不好了,这个要看架构的整体倾向性,要安全,还是透明性
在这一模式中,相对于安全性,我们比较强调透明性。对于第一种方式中叶子节点内不需要的方法可以使用空处理或者异常报告的方式来解决。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线----------------------------------------------------------
8.装饰者模式
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比生成子类更为灵活
这个设计模式呢 和适配器模式不同,适配器强调对接口名称的转换,而装饰者强调对接口逻辑的改变。
他的做法是有一个装饰者的类,里面有一个被装饰者的引用,这个类通常都是和被装饰者继承相同或实现相同,可以直接装饰(修改逻辑),也可以让子类继承来修改逻辑
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线----------------------------------------------------------
9.门面模式
为子系统中的一组接口提供一个一致的界面, Facade模式定义了一个高层接口,这个接口使得这
一子系统更加容易使用。
就是类似于面向对象封装的思想,把错综复杂的api整理隔离,使其更易于使用。相当于一个大公司,有事应该去找前台。。。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线----------------------------------------------------------
10.享元模式
采用一个共享类来避免大量拥有相同内容的“小类”的开销。这种开销中最常见、直观的影响就是增加了内存的损耗。享元模式以共享的方式高效的支持大量的细粒度对象,减少其带来的开销
在享元模式中可以共享的相同内容称为 内部状态(Intrinsic State),而那些需要外部环境来设置的不能共享的内容称为 外部状态(Extrinsic State),其中外部状态和内部状态是相互独立的,外部状态的变化不会引起内部状态的变化。由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。也就是说,享元模式的本质是分离与共享 : 分离变与不变,并且共享不变。把一个对象的状态分成内部状态和外部状态,内部状态即是不变的,外部状态是变化的;然后通过共享不变的部分,达到减少对象数量并节约内存的目的。
在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池(Flyweight Pool)(用于存储具有相同内部状态的享元对象)。在享元模式中,共享的是享元对象的内部状态,外部状态需要通过环境来设置。在实际使用中,能够共享的内部状态是有限的,因此享元对象一般都设计为较小的对象,它所包含的内部状态较少,这种对象也称为 细粒度对象。
享元模式的目的就是使用共享技术来实现大量细粒度对象的复用。
它通常和工厂模式复用,它的含义就像我获取1000个对象,但是这1000个对象的内部状态是同一个引用,而外部状态更像静态方法,不对内部状态做修改。这样1000个对象同一个引用会节省大量内存。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线----------------------------------------------------------
11.代理模式
为其他对象提供一种代理以控制对这个对象的访问。比如说动态代理,反向代理,这个就不多说了。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线----------------------------------------------------------
12.责任链模式
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
比如说servlet的过滤器链,strust的拦截器链。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线----------------------------------------------------------
13.命令模式
在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
这个模式有3个角色:
- Receiver接受者角色:该角色就是干活的角色,命令传递到这里是应该被执行的
- Command命令角色:需要执行的所有命令都在这里声明
- Invoker调用者角色:接收到命令,并执行命令
14.迭代器模式
提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。而且最好还能保证每一次迭代的内部状态的唯一性。
他的做法是弄一个内部类专门用来遍历接口的封装,每遍历一次迭代器都是不同的,从新new 因为重新new能保证每一次的数据是独立的,保证每一次迭代的状态不冲突。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线----------------------------------------------------------
15.备忘录模式
备忘录(Memento)模式又称标记(Token)模式。GOF 给备忘录模式的定义为:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
他的使用场景就是我一个对象里面会有特别多的信息要备份更新恢复,这时候我们要使用内部的一个机制来保存这个状态,这种机制可能是一个内部类,总之要减少备份更新恢复的开销,否则这种设计模式也没啥意义了。
它有三部分组成:
1) 备忘录(Memento)角色:备忘录角色存储“备忘发起角色”的内部状态。
2) 备忘发起(Originator)角色:“备忘发起角色”创建一个备忘录,用以记录当前时刻它
的内部状态。在需要时使用备忘录恢复内部状态。
3) 备忘录管理者(Caretaker)角色:负责保存好备忘录。不能对备忘录的内容进行操作
或检查。
他的基本逻辑是这样的:
我需要使用一个对象来存储某一时刻对象的状态,其中最好使用内部类来保存状态,如果使用宽接口来控制管理者角色,则感觉力度不够,即向下转型即可破坏封装性,我们希望备忘录发起者操作备忘录管理者,最好只有它能操作,这是封装性的提现,自己的属性状态不希望其他对象操作。
class Originator{
//这个是要保存的状态
private int state= 90;
//保持一个“备忘录管理者角色”的对象
private Caretaker c = new Caretaker();
//读取备忘录角色以恢复以前的状态
public void setMemento(){
Memento memento = (Memento)c.getMemento();
state = memento.getState();
System.out.println("the state is "+state+" now");
}
//创建一个备忘录角色,并将当前状态属性存入,托给“备忘录管理者角色”存放。
public void createMemento(){
c.saveMemento(new Memento(state));
}
//this is other business methods...
//they maybe modify the attribute state
public void modifyState4Test(int m){
state = m;
System.out.println("the state is "+state+" now");
}
//作为私有内部类的备忘录角色,它实现了窄接口,可以看到在第二种方法中宽接
口已经不再需要
//注意:里面的属性和方法都是私有的
private class Memento implements MementoIF{
private int state ;
private Memento(int state){
this.state = state ;
}
private int getState(){
return state;
}
}
}
//测试代码——客户程序
public class TestInnerClass{
public static void main(String[] args){
Originator o = new Originator();
o.createMemento();
o.modifyState4Test(80);
o.setMemento();
}
}
//窄接口
interface MementoIF{}
//“备忘录管理者角色”
class Caretaker{
private MementoIF m ;
public void saveMemento(MementoIF m){
this.m = m;
}
public MementoIF getMemento(){
return m;
}
}
一句话:备忘录发起者创建备忘录到备忘录管理者
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线----------------------------------------------------------
16.观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
这种设计模式我见过的场景是RxJs即Angular开发时候使用过,安卓开发时RxJava配合retrofit2,是一种事件的发布与订阅的设计模式。
这个设计模式就是:被观察者状态改变则需要通知(调接口)观察者进行某种操作
举个例子
/**
* @author 赵建新
* @date 2018/4/16 14:11
*/
public class ObserverDemo {
public static void main(String[] strings) {
Observerable observerable = new ObserverableImpl();
observerable.registerObserver(new ObserverImpl());
observerable.notifyObserver("Hello World");
}
}
/**
* 被观察者
* 接口
*/
interface Observerable {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObserver(String message);
}
class ObserverableImpl implements Observerable {
private List<Observer> list = new LinkedList<>();
@Override
public void registerObserver(Observer o) {
list.add(o);
}
@Override
public void removeObserver(Observer o) {
list.remove(o);
}
@Override
public void notifyObserver(String message) {
list.forEach(observer -> {
observer.update(message);
});
}
}
/**
* 观察者接口
*/
interface Observer {
void update(String message);
}
class ObserverImpl implements Observer {
@Override
public void update(String message) {
System.out.println(message);
}
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线----------------------------------------------------------
17.策略模式
策略模式呢?我们常用的是jdbcTemplate的封装自定义结果集,策略模式就是提供一个接口,可以用户自定义去实现,包括匿名内部类。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线----------------------------------------------------------
18.模板模式
这个模式就是在父类定义,默认实现,子类可以随心所欲的覆盖这个实现,最著名的的例子就是:HttpServlet
这个方法的默认实现就是一堆错误,但是覆盖的实现通常是框架做的,如Spring Mvc。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线----------------------------------------------------------
19.状态模式
这个就是通过对象内部的状态改变对象的行为,这种行为的改变一般都是多态实现的,因为通常if重构都是重构成多态形式。
而多态模式通常是把行为包装成参数,当赋值时候赋值的是一个行为。这样就可以实现if-->多态了。
比如说:
public class DuoTaiDemo {
public static void main(String[] strings) {
Context context = new Context();
context.setLeft(Context.liftA);
context.method_A();
}
}
abstract class Lift {
public abstract void method_A();
public abstract void method_B();
}
class Context {
public final static Lift liftA = new LiftA();
public final static Lift liftB = new LiftB();
private Lift left;
public void setLeft(Lift left) {
this.left = left;
}
public void method_A() {
left.method_A();
}
public void method_B() {
left.method_B();
}
}
class LiftA extends Lift {
@Override
public void method_A() {
System.out.println("LiftA::method_A");
}
@Override
public void method_B() {
System.out.println("LiftA::method_B");
}
}
class LiftB extends Lift {
@Override
public void method_A() {
System.out.println("LiftB::method_A");
}
@Override
public void method_B() {
System.out.println("LiftB::method_B");
}
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------牛逼的分割线----------------------------------------------------------
20.访问者模式
访问者模式,顾名思义使用了这个模式后就可以在不修改已有程序结构的前提下,通过添加额外的“访问者”来完成对已有代码功能的提升。
常常用于数据结构稳定,作用于数据结构的操作经常变化的时候。而这种设计模式如何做的呢?
1.在被访问者设置方法传入访问者接口
2.在被访问者设置的方法中调用访问者的方法传入自己的引用或者私有数据
通过方法传参的方式让其他的类能够访问自己私有数据,这就是访问者模式的意义。但是封装性呢?这是要取舍和考虑的部分。