为什么要用设计模式

设计模式是对软件设计中普遍存在的各种问题,所提出的解决方案。可以提高项目的维护性(可读性,规范性),高内聚,低耦合,可扩展性,可靠性

单一设计原则

即一个类应该只负责一项职责

  • 降低类的复杂度,一个类只负责一项职责
  • 提高类的可读性,可维护性
  • 降低变更引起的风险
  • 只有逻辑足够简单,才可以违反单一职责原则;只有类中的方法足够少,才可以在方法级别上执行单一职责原则

接口隔离原则

客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
将接口拆分为几个独立的接口,类分别与需要的接口建立依赖关系。

依赖倒转原则

  • 高层模块不要依赖底层模块,二者都应该依赖类或者接口
  • 抽象不应该依赖细节,细节才去依赖抽象
  • 中心思想:面向接口编程
  • 设计理念:相对于细节的多变性,抽象要稳定的多。以抽象为基础的架构比以细节为基础的架构要稳定的多。抽象指的是接口或者抽象类,细节指的就是实现类
  • 使用接口或抽象类的目的时制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成

依赖关系传递

  1. 通过接口传递实现依赖
interface IOpenAndClose{public void open(ITV tv);
}
interface ITV{public void play();
}
//实现接口
class OpenAndClose implements IOpenAndClose{public void open(ITV tv){tv.play();}
}
  1. 通过setter方法传递
  2. 通过构造方法传递

注意事项 1:低层模块尽量要有抽象类或接口,稳定性更好
2:变量的声明类型尽量是抽象类或接口,这样变量引用时就存在一个缓冲层,利于程序扩展和优化
继承时遵循里氏替换原则

里氏替换原则

基本介绍:所有引用基类的地方必须能透明地使用其子类的对象

  • 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
  • 继承实际上让两个类耦合性增强了,在适当的情况下。可以通过聚合,组合,依赖来解决问题。

我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差,特别是运行多态比较频繁时。

通常的解决办法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等关系替代。

开闭原则

  • 编程中最基础,最重要的设计原则。
  • 一个软件实现类,模块和函数用扩展开发(提供方),对修改关闭(使用方)
  • 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有代码

迪米特法则(最小知道原则)

  • 一个对象应该对其他对象保持最少的了解
  • 类与类关系越密切,耦合度越大
  • 只与直接的朋友通信 ,直接的朋友:只要两个对象之间存在耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式有很多:依赖,关联,组合,聚合。

目的是降低耦合,而不是完全没有耦合

合成复用原则

原则是尽量使用合成/聚合的方式,而不是使用继承

设计模式类型

创建型模式:单例模式,抽象工厂模式,原型模式,建造者模式,工厂模式
结构型模式:适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式
行为型模式:模板方法模式,命令模式,访问者模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,职责链模式

单例模式

饿汉式:可能会造成内存浪费(预加载)

//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {private Singleton1() {}private static final Singleton1 single = new Singleton1();//静态工厂方法 public static Singleton1 getInstance() {return single;}
}

懒汉式

//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {private Singleton() {}private static Singleton single=null;//静态工厂方法 public static Singleton getInstance() {if (single == null) {  single = new Singleton();}  return single;}
}

但是这种懒汉式线程是不安全的

所有又有了同步方法的懒汉式

//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {private Singleton() {}private static volatile Singleton single;//静态工厂方法 //双重检查代码,解决线程安全问题,同时解决懒加载问题public static Singleton getInstance() {if (single == null) { synchronized (Singleton.class){if(single==null){single = new Singleton();}}}  return single;}
}

这种单例设计模式,线程安全;延迟加载;效率较高

还有两种单例模式分别是静态代码块,枚举

单例模式使用场景:需要频繁的进行创建和销毁对象,创建对象时耗时过多或耗费资源过多,但又经常用到的对象,工具类对象,频繁访问数据库或文件的对象(比如数据源,session工厂等)

简单工厂模式

定义:定义了一个创建对象的类,由这个类封装实例化对象的行为

以一个pizza工厂作为例子,这个工厂生产三种类型的pizza:chesse,pepper,greek

简单工厂类

public class SimplePizzaFactory {public Pizza CreatePizza(String ordertype) {Pizza pizza = null;if (ordertype.equals("cheese")) {pizza = new CheesePizza();} else if (ordertype.equals("greek")) {pizza = new GreekPizza();} else if (ordertype.equals("pepper")) {pizza = new PepperPizza();}return pizza;}
}

简单工厂存在的问题与解决方法: 简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了开闭原则。我们可以定义一个创建对象的抽象方法并创建多个不同的工厂类实现该抽象方法,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。这种方法也就是我们接下来要说的工厂方法模式。

工厂方法模式

定义:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。
如果我们想新增一个pizza产地,就无需改动上面的代码,只需要新增一个工厂类

public class BJOrderPizza extends OrderPizza {Pizza createPizza(String ordertype) {Pizza pizza = null;if (ordertype.equals("cheese")) {pizza = new LDCheesePizza();} else if (ordertype.equals("pepper")) {pizza = new LDPepperPizza();}return pizza;}
}

工厂方法存在的问题与解决方法:客户端需要创建类的具体的实例。简单来说就是用户要订某个工厂的pizza必须要到那个工厂去。 当工厂发生变化了,用户也要跟着变化,这无疑就增加了用户的操作复杂性。为了解决这一问题,我们可以把工厂类抽象为接口,用户只需要去找默认的工厂提出自己的需求(传入参数),便能得到自己想要产品,而不用根据产品去寻找不同的工厂,方便用户操作。这也就是我们接下来要说的抽象工厂模式。

抽象工厂模式

定义:定义了一个接口用于创建相关或有依赖关系的对象类,而无需明确指定具体类

那么我们给上面的工厂提供一个接口:

public interface AbsFactory {Pizza CreatePizza(String ordertype) ;
}

工厂的实现

public class LDFactory implements AbsFactory {@Overridepublic Pizza CreatePizza(String ordertype) {Pizza pizza = null;if ("cheese".equals(ordertype)) {pizza = new LDCheesePizza();} else if ("pepper".equals(ordertype)) {pizza = new LDPepperPizza();}return pizza;}
}

pizzaStore的实现


public class PizzaStroe {public static void main(String[] args) {OrderPizza mOrderPizza;mOrderPizza = new OrderPizza("London");}
}

解决了工厂方法模式的问题:在抽象工厂中PizzaStroe中只需要传入参数就可以实例化对象。

原型模式

定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象

浅拷贝

  • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
  • 对于数据类型是引用数据类型的成员变量,浅拷贝会进行引用传递,也就是将该成员变量的引用值复制一份给新的对象,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值

深拷贝
将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。clone明显是深复制,clone出来的对象是是不能去影响原型对象的

原型模式的结构

  • Client:使用者
  • Prototype:接口(抽象类),声明具备clone能力,例如java中得Cloneable接口
  • ConcretePrototype:具体的原型类
public class Prototype implements Cloneable {  public Object clone() throws CloneNotSupportedException {  Prototype proto = (Prototype) super.clone();  return proto;  }
}

原型模式的本质就是clone,可以解决构建复杂对象的资源消耗问题,能再某些场景中提升构建对象的效率;还有一个重要的用途就是保护性拷贝,可以通过返回一个拷贝对象的形式,实现只读的限制。

建造者模式

定义:建造者模式又叫生成者模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来。
角色

在这样的设计模式中,有以下几个角色:

  1. Builder:为创建一个产品对象的各个部件指定抽象接口。
  2. ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口。
  3. Director:构造一个使用Builder接口的对象,指导构建过程。
  4. Product:表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程,包含定义组成部件的类,包括将这些部件装配成最终产品的接口。

流程:

  1. 指挥者直接与客户进行需求沟通
  2. 沟通后指挥者将用户创建产品的需求划分为各个部件的建造请求(Builder)
  3. 将各个部件的建造请求委派到具体的建造者(ConcreteBuilder)
  4. 各个具体建造者负责进行产品部件的构建
  5. 最终构建成具体产品

那以做一份糖水作为例子吧

  1. 定义制作一份糖水的过程

public  abstract class Builder {  //第一步:准备原料
//声明为抽象方法,具体由子类实现 public abstract void  RawMaterial();//第二步:烹调
//声明为抽象方法,具体由子类实现 public abstract void Cook();//第三步:装盘
//声明为抽象方法,具体由子类实现 public abstract void Put();//返回产品的方法:获得糖水public abstract Syrup GetSyrup();
}
  1. 客户下单通知厨师
public class Director{//指挥厨师public void Construct(Builder builder){builder.RawMaterial();builder.Cook();builder.Put();}}
  1. 创建具体的建造者(ConcreteBuilder)
//装机人员1public class ConcreteBuilder extend  Builder{//创建产品实例Syrup syrup = new Syrup();//组装产品@Overridepublic void  RawMaterial(){  syrup.Add("原料")}  @Overridepublic void  Cook(){  syrup.Add("烹饪")}  @Overridepublic void  Put(){  syrup.Add("装盘")}  //返回@Overridepublic  Syrup GetSyrup(){  return Syrup;}
}
  1. 定义具体产品类
public class Syrup{//集合private List<String> parts = new ArrayList<String>();//用于将组件组装到电脑里public void Add(String part){parts.add(part);
}public void Show(){for (int i = 0;i<parts.size();i++){    System.out.println(“组件”+parts.get(i)+“装好了”);}}}
  1. 客户端调用

public class Builder Pattern{public static void main(String[] args){//下单Director director = new Director();Builder builder = new ConcreteBuilder();//通知厨师director.Construct(builder);//okSyrup syrup = builder.GetSyrup();}
}

代理模式

定义:代理模式的特征是代理类和委托类有同样的接口,代理类主要负责为委托类预处理消息,过滤信息,把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性。


JDK动态代理

/*** JDK动态代理实现InvocationHandler接口*/
public class JdkProxy implements InvocationHandler {private Object targetObject;  //需要代理的目标对象//定义获取代理对象的方法(将目标对象传入进行代理)public Object getJDKProxy(Object targetObject){//为目标对象target赋值this.targetObject = targetObject;//JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出Object proxyObject = Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);//返回代理对象return proxyObject;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("JDK动态代理,监听开始!");// 调用invoke方法,result存储该方法的返回值Object result = method.invoke(targetObject,args);System.out.println("JDK动态代理,监听结束!");return result;}}

上面使用的InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序。

当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用,如下所示

    /*** proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0* method:我们所要调用某个对象真实的方法的Method对象* args:指代代理对象方法传递的参数*/public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但最常用的式newProxyInstance方法。

public static Object newProxyInstance(ClassLoader loader, Class<?>[]【interfaces, InvocationHandler h)

这个方法的作用就是创建一个代理类对象:

  • loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
  • interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
  • h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。

除了JDK实现,还有CGLIB实现


//Cglib动态代理,实现MethodInterceptor接口
public class CglibProxy implements MethodInterceptor {private Object target;//需要代理的目标对象//重写拦截方法@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("Cglib动态代理,监听开始!");Object result = method.invoke(target,args);//方法执行参数:target 目标对象 arr参数数组System.out.println("Cglib动态代理,监听结束!");return result;}//定义获取代理对象的方法public UserManager getCglibProxy(Object targetObject) {this.target = targetObject;//为目标对象target赋值Enhancer enhancer = new Enhancer();//设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类enhancer.setSuperclass(targetObject.getClass()); //UserManagerImplenhancer.setCallback(this);//设置回调Object result = enhancer.create();//创建并返回代理对象return (UserManager) result;}
}

适配器模式

适配器模式的主要作用是在新接口和老接口之间进行适配。它非常像我们出国旅行时带的电源转换器。
uu
模式中的角色:

  • 目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
  • 需要适配的类(Adaptee):需要适配的类或适配者类。
  • 适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。

实现方式

  • 类的适配器模式(采用继承实现)
  • 对象适配器(采用对象组合方式实现)

类的适配器

// 已存在的、具有特殊功能、但不符合我们既有的标准接口的类
class Adaptee {public void specificRequest() {System.out.println("被适配类具有 特殊功能...");}
}// 目标接口,或称为标准接口
interface Target {public void request();
}// 具体目标类,只提供普通功能
class ConcreteTarget implements Target {public void request() {System.out.println("普通类 具有 普通功能...");}
}// 适配器类,继承了被适配类,同时实现标准接口
class Adapter extends Adaptee implements Target{public void request() {super.specificRequest();}
}// 测试类public class Client {public static void main(String[] args) {// 使用普通功能类Target concreteTarget = new ConcreteTarget();concreteTarget.request();// 使用特殊功能类,即适配类Target adapter = new Adapter();adapter.request();}
}

对象适配器:
不是使用多继承或继承再实现的方式,而是使用委托的方式

// 适配器类,直接关联被适配类,同时实现标准接口
class Adapter implements Target{// 直接关联被适配类private Adaptee adaptee;// 可以通过构造函数传入具体需要适配的被适配类对象public Adapter (Adaptee adaptee) {this.adaptee = adaptee;}public void request() {// 这里是使用委托的方式完成特殊功能this.adaptee.specificRequest();}
}// 测试类
public class Client {public static void main(String[] args) {// 使用普通功能类Target concreteTarget = new ConcreteTarget();concreteTarget.request();// 使用特殊功能类,即适配类,// 需要先创建一个被适配类的对象作为参数Target adapter = new Adapter(new Adaptee());adapter.request();}
}

适配器模式适用场景:
1.系统需要使用现有的类,而这些类的接口不符合系统的接口
2.想要建立一个可以重用的类,用于与一些彼此没有太大关联的一些类,包括一些可能在将来引用的类一起工作
3.两个类所做的事情相同或相似,但是具有不同接口的时候
4.旧的系统开发的类已经实现了一些功能,但是客户端却只能以另外接口的形式访问,但我们不希望手动更改原有类的时候
5.使用第三方组件,组件接口定义和自己定义的不同,不希望修改自己的接口,但是要使用第三方组件接口的功能

装饰者模式

定义:动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用装饰者模式相比用生成子类方式达到功能的扩充显得更为灵活。

//定义被装饰者
public interface Human {public void wearClothes();public void walkToWhere();
}
//定义装饰者
public abstract class Decorator implements Human {private Human human;public Decorator(Human human) {this.human = human;}public void wearClothes() {human.wearClothes();}public void walkToWhere() {human.walkToWhere();}
}
//细化装饰者
public class smallDecorator extends Decorator {public smallDecorator(Human human) {super(human);}public void goHome() {System.out.println("进房子");}@Overridepublic void wearClothes() {// TODO Auto-generated method stubsuper.wearClothes();goHome();}@Overridepublic void walkToWhere() {// TODO Auto-generated method stubsuper.walkToWhere();goHome();}
}

总结:装饰者和被装饰者之间必须是一样的类型,也就是要有共同的超类。在这里应用继承并不是实现方法的复制,而是实现类型的匹配。因为装饰者和被装饰者是同一个类型,因此装饰者可以取代被装饰者,这样就使被装饰者拥有了装饰者独有的行为。根据装饰者模式的理念,我们可以在任何时候,实现新的装饰者增加新的行为。如果是用继承,每当需要增加新的行为时,就要修改原程序了。

外观模式

以医院作为例子,如果把医院作为一个系统,按照部门职能,这个系统可以划分为挂号、门诊、划价、化验、收费、取药等。看病的病人要与这些部门打交道,就如同一个子系统的客户端与一个子系统的各个类打交道一样,不是一件容易的事情。首先病人必须先挂号,然后门诊。如果医生要求化验,病人必须首先划价,然后缴费,才可以到化验部门做化验。化验后再回到门诊室。

解决这种不便的方法便是引进外观模式,医院可以设置一个接待员的位置,由接待员负责代为挂号、划价、缴费、取药等。这个接待员就是外观模式的体现,病人只接触接待员,由接待员与各个部门打交道。

外观模式的对象:

  • 外观(Facade)角色 :客户端可以调用这个角色的方法。此角色知晓相关的(一个或者多个)子系统的功能和责任。在正常情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去。
  • 子系统(SubSystem)角色 :可以同时有一个或者多个子系统。每个子系统都不是一个单独的类,而是一个类的集合。每个子系统都可以被客户端直接调用,或者被外观角色调用。子系统并不知道外观的存在,对于子系统而言,外观仅仅是另外一个客户端而已。

子系统的角色

public class ModuleA {//示意方法public void testA(){System.out.println("调用ModuleA中的testA方法");}
}
public class ModuleB {//示意方法public void testB(){System.out.println("调用ModuleB中的testB方法");}
}

外观角色类

public class Facade {//示意方法,满足客户端需要的功能public void test(){ModuleA a = new ModuleA();a.testA();ModuleB b = new ModuleB();b.testB();ModuleC c = new ModuleC();c.testC();}
}

桥接模式

定义:就是用来连接两个部分,使得两个部分可以互相通讯或者使用,桥接模式的作用就是为被分离了的抽象部分和实现部分搭桥

  • Abstraction:抽象部分,该类保持一个对实现部分对象的引用,抽象部分中的方法需要调用实现部分的对象来实现,该类一般为抽象类;
  • RefinedAbstraction:优化的抽象部分,抽象部分的具体实现,该类一般对抽象部分的方法进行完善和扩展;
  • Implementor:实现部分,可以为接口或者是抽象类,其方法不一定要与抽象部分中的一致,一般情况下是由实现部分提供基本的操作,而抽象部分定义的则是基于实现部分基本操作的业务方法;
  • ConcreteImplementorA 和 ConcreteImplementorB :实现部分的具体实现,完善实现部分中定义的具体逻辑。

Implementor

public interface Implementor {void operationImpl();
}

ConcreteImplementorA

public class ConcreteImplementorA implements Implementor{@Overridepublic void operationImpl() {//具体实现}
}

ConcreteImplementorB

public class ConcreteImplementorB implements Implementor{@Overridepublic void operationImpl() {//具体实现}
}

Abstraction

public abstract class Abstraction {private Implementor implementor;public Abstraction(Implementor implementor) {this.implementor = implementor;}public void operation() {implementor.operationImpl();}
}

RefinedAbstraction

public class RefinedAbstraction extends Abstraction{public RefinedAbstraction(Implementor implementor) {super(implementor);}public void refinedOperation() {//对 Abstraction 中的 operation 方法进行扩展}
}

适配器 && 装饰者 && 桥接 && 代理

适配器模式
适配器模式和其他三个设计模式一般不容易搞混,它的作用是将原来不兼容的两个类融合在一起,uml 图也和其他的差别很大。

装饰者模式
装饰者模式结构上类似于代理模式,但是和代理模式的目的是不一样的,装饰者是用来动态地给一个对象添加一些额外的职责,装饰者模式为对象加上行为,而代理则是控制访问。


桥接模式
桥接模式的目的是为了将抽象部分与实现部分分离,使他们都可以独立地进行变化,所以说他们两个部分是独立的,没有实现自同一个接口,这是桥接模式与代理模式,装饰者模式的区别。

代理模式
 代理模式为另一个对象提供代表,以便控制客户对对象的访问,管理的方式有很多种,比如远程代理和虚拟代理等,这个在上面有,这里就不说了,而装饰者模式则是为了扩展对象。

组合模式

定义:将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性
何时使用:

  1. 您想表示对象的部分-整体层次结构(树形结构)。
  2. 您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

组合模式的主要优点有:

  • 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
  • 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;

组合模式角色:

  • 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。
  • 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口。
  • 树枝构件(Composite)角色:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含
    Add()、Remove()、GetChild() 等方法

享元模式

定义:通过共享的方式高效的支持大量细粒度的对象。
何时使用:

  1. 系统中有大量对象。
  2. 这些对象消耗大量内存。
  3. 这些对象的状态大部分可以外部化。
  4. 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替
  5. 系统不依赖于这些对象身份,这些对象是不可分辨的。

享元模式对象:

  • Flyweight
    (享元抽象类):一般是接口或者抽象类,定义了享元类的公共方法。这些方法可以分享内部状态的数据,也可以调用这些方法修改外部状态。
  • ConcreteFlyweight(具体享元类):具体享元类实现了抽象享元类的方法,为享元对象开辟了内存空间来保存享元对象的内部数据,同时可以通过和单例模式结合只创建一个享元对象。
  • FlyweightFactory(享元工厂类):享元工厂类创建并且管理享元类,享元工厂类针对享元类来进行编程,通过提供一个享元池来进行享元对象的管理。一般享元池设计成键值对,或者其他的存储结构来存储。当客户端进行享元对象的请求时,如果享元池中有对应的享元对象则直接返回对应的对象,否则工厂类创建对应的享元对象并保存到享元池。

模板方法模式

定义:定义一个操作中的算法骨架,而将一些步骤延迟到子类中实现,使得子类可以不改变该算法结构的情况下重新定义该算法的某些步骤

模板方法(Template Method)模式包含以下主要角色:

  • 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。

    • 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。

    • 基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:

      • 抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。

      • 具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。

      • 钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。

        一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。

  • 具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。

package com.yuzihao;public abstract class abstractClass {public final void cookProcess() {//准备材料this.prepared();//加入原料this.add();//开煮this.fry();}public void prepared() {System.out.println("准备材料");}//第三步:加入原料是不一样的public abstract void add();public void fry(){System.out.println("开煮");}
}public class Green extends  abstractClass {@Overridepublic void add() {System.out.println("加入绿豆");}}public class Red extends abstractClass {@Overridepublic void add() {System.out.println("加入红豆");}}public class Client {public static void main(String[] args) {//炒手撕包菜Green green = new Green();green.cookProcess();//炒蒜蓉菜心Red red = new Red();red.cookProcess();}
}

优点:

  • 实现了反向控制

    通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制 ,并符合“开闭原则”。

策略模式

定义:该模式定义了一系列算法,并将每个算法封装起来,是他们可以相互替换,且算法的变化不会影响使用算法的客户

策略模式的主要角色如下:

  • 抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
  • 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
  • 环境(Context)类:持有一个策略类的引用,最终给客户端调用。

共同接口

public interface Strategy {void show();
}

具体策略角色

public class StrategyA implements Strategy {public void show() {System.out.println("买一送一");}
}public class StrategyB implements Strategy {public void show() {System.out.println("满200元减50元");}
}

定义环境角色

public class SalesMan {                        //持有抽象策略角色的引用                              private Strategy strategy;                 public SalesMan(Strategy strategy) {       this.strategy = strategy;              }                                          //向客户展示促销活动                                public void salesManShow(){                strategy.show();                       }
}

优点:

  • 策略类之间可以自由切换

    由于策略类都实现同一个接口,所以使它们之间可以自由切换。

  • 易于扩展

    增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合“开闭原则“

  • 避免使用多重条件选择语句(if else),充分体现面向对象设计思想。

2,缺点:

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
  • 策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。

命令模式

将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理。

命令模式包含以下主要角色:

  • 抽象命令类(Command)角色: 定义命令的接口,声明执行的方法。
  • 具体命令(Concrete Command)角色:具体的命令,实现命令接口;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
  • 实现者/接收者(Receiver)角色: 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
  • 调用者/请求者(Invoker)角色: 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。

1,优点:**

  • 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
  • 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
  • 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
  • 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。

责任链模式

定义:又名职责链模式,为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

职责链模式主要包含以下角色:

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

具体实例

//请假条
public class LeaveRequest {private String name;//姓名private int num;//请假天数private String content;//请假内容public LeaveRequest(String name, int num, String content) {this.name = name;this.num = num;this.content = content;}public String getName() {return name;}public int getNum() {return num;}public String getContent() {return content;}
}//处理者抽象类
public abstract class Handler {protected final static int NUM_ONE = 1;protected final static int NUM_THREE = 3;protected final static int NUM_SEVEN = 7;//该领导处理的请假天数区间private int numStart;private int numEnd;//领导上面还有领导private Handler nextHandler;//设置请假天数范围 上不封顶public Handler(int numStart) {this.numStart = numStart;}//设置请假天数范围public Handler(int numStart, int numEnd) {this.numStart = numStart;this.numEnd = numEnd;}//设置上级领导public void setNextHandler(Handler nextHandler){this.nextHandler = nextHandler;}//提交请假条public final void submit(LeaveRequest leave){if(0 == this.numStart){return;}//如果请假天数达到该领导者的处理要求if(leave.getNum() >= this.numStart){this.handleLeave(leave);//如果还有上级 并且请假天数超过了当前领导的处理范围if(null != this.nextHandler && leave.getNum() > numEnd){this.nextHandler.submit(leave);//继续提交} else {System.out.println("流程结束");}}}//各级领导处理请假条方法protected abstract void handleLeave(LeaveRequest leave);
}//小组长
public class GroupLeader extends Handler {public GroupLeader() {//小组长处理1-3天的请假super(Handler.NUM_ONE, Handler.NUM_THREE);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");System.out.println("小组长审批:同意。");}
}//部门经理
public class Manager extends Handler {public Manager() {//部门经理处理3-7天的请假super(Handler.NUM_THREE, Handler.NUM_SEVEN);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");System.out.println("部门经理审批:同意。");}
}//总经理
public class GeneralManager extends Handler {public GeneralManager() {//部门经理处理7天以上的请假super(Handler.NUM_SEVEN);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");System.out.println("总经理审批:同意。");}
}//测试类
public class Client {public static void main(String[] args) {//请假条来一张LeaveRequest leave = new LeaveRequest("小花",5,"身体不适");//各位领导GroupLeader groupLeader = new GroupLeader();Manager manager = new Manager();GeneralManager generalManager = new GeneralManager();groupLeader.setNextHandler(manager);//小组长的领导是部门经理manager.setNextHandler(generalManager);//部门经理的领导是总经理//之所以在这里设置上级领导,是因为可以根据实际需求来更改设置,如果实战中上级领导人都是固定的,则可以移到领导实现类中。//提交申请groupLeader.submit(leave);}
}

状态模式

定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

//抽象状态类
public abstract class LiftState {//定义一个环境角色,也就是封装状态的变化引起的功能变化protected Context context;public void setContext(Context context) {this.context = context;}//电梯开门动作public abstract void open();//电梯关门动作public abstract void close();//电梯运行动作public abstract void run();//电梯停止动作public abstract void stop();
}//开启状态
public class OpenningState extends LiftState {//开启当然可以关闭了,我就想测试一下电梯门开关功能@Overridepublic void open() {System.out.println("电梯门开启...");}@Overridepublic void close() {//状态修改super.context.setLiftState(Context.closeingState);//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().close();}//电梯门不能开着就跑,这里什么也不做@Overridepublic void run() {//do nothing}//开门状态已经是停止的了@Overridepublic void stop() {//do nothing}
}//运行状态
public class RunningState extends LiftState {//运行的时候开电梯门?你疯了!电梯不会给你开的@Overridepublic void open() {//do nothing}//电梯门关闭?这是肯定了@Overridepublic void close() {//虽然可以关门,但这个动作不归我执行//do nothing}//这是在运行状态下要实现的方法@Overridepublic void run() {System.out.println("电梯正在运行...");}//这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了@Overridepublic void stop() {super.context.setLiftState(Context.stoppingState);super.context.stop();}
}//停止状态
public class StoppingState extends LiftState {//停止状态,开门,那是要的!@Overridepublic void open() {//状态修改super.context.setLiftState(Context.openningState);//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().open();}@Overridepublic void close() {//虽然可以关门,但这个动作不归我执行//状态修改super.context.setLiftState(Context.closeingState);//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().close();}//停止状态再跑起来,正常的很@Overridepublic void run() {//状态修改super.context.setLiftState(Context.runningState);//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().run();}//停止状态是怎么发生的呢?当然是停止方法执行了@Overridepublic void stop() {System.out.println("电梯停止了...");}
}//关闭状态
public class ClosingState extends LiftState {@Override//电梯门关闭,这是关闭状态要实现的动作public void close() {System.out.println("电梯门关闭...");}//电梯门关了再打开,逗你玩呢,那这个允许呀@Overridepublic void open() {super.context.setLiftState(Context.openningState);super.context.open();}//电梯门关了就跑,这是再正常不过了@Overridepublic void run() {super.context.setLiftState(Context.runningState);super.context.run();}//电梯门关着,我就不按楼层@Overridepublic void stop() {super.context.setLiftState(Context.stoppingState);super.context.stop();}
}//环境角色
public class Context {//定义出所有的电梯状态public final static OpenningState openningState = new OpenningState();//开门状态,这时候电梯只能关闭public final static ClosingState closeingState = new ClosingState();//关闭状态,这时候电梯可以运行、停止和开门public final static RunningState runningState = new RunningState();//运行状态,这时候电梯只能停止public final static StoppingState stoppingState = new StoppingState();//停止状态,这时候电梯可以开门、运行//定义一个当前电梯状态private LiftState liftState;public LiftState getLiftState() {return this.liftState;}public void setLiftState(LiftState liftState) {//当前环境改变this.liftState = liftState;//把当前的环境通知到各个实现类中this.liftState.setContext(this);}public void open() {this.liftState.open();}public void close() {this.liftState.close();}public void run() {this.liftState.run();}public void stop() {this.liftState.stop();}
}
public class Client {public static void main(String[] args) {Context context = new Context();context.setLiftState(new ClosingState());context.open();context.close();context.run();context.stop();}
}

优点:

  • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。

发布订阅模式

定义:让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

在观察者模式中有如下角色:

  • Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
  • ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
  • Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
  • ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

抽象观察者

public interface Observer {void update(String message);
}

具体观察者

public class WeixinUser implements Observer {// 微信用户名private String name;public WeixinUser(String name) {this.name = name;}@Overridepublic void update(String message) {System.out.println(name + "-" + message);}
}

抽象被观察者

public interface Subject {//增加订阅者public void attach(Observer observer);//删除订阅者public void detach(Observer observer);//通知订阅者更新消息public void notify(String message);
}

具体被观察者

public class SubscriptionSubject implements Subject {//储存订阅公众号的微信用户private List<Observer> weixinUserlist = new ArrayList<Observer>();@Overridepublic void attach(Observer observer) {weixinUserlist.add(observer);}@Overridepublic void detach(Observer observer) {weixinUserlist.remove(observer);}@Overridepublic void notify(String message) {for (Observer observer : weixinUserlist) {observer.update(message);}}
}

在 Java 中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。

1,Observable类

Observable 类是抽象目标类(被观察者),它有一个 Vector 集合成员变量,用于保存所有要通知的观察者对象,下面来介绍它最重要的 3 个方法。

  • void addObserver(Observer o) 方法:用于将新的观察者对象添加到集合中。

  • void notifyObservers(Object arg) 方法:调用集合中的所有观察者对象的 update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。

  • void setChange() 方法:用来设置一个 boolean 类型的内部标志,注明目标对象发生了变化。当它为true时,notifyObservers() 才会通知观察者。

2,Observer 接口

Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 update 方法,进行相应的工作。

中介者模式

定义:又叫调停模式,定义一个中介角色来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。

中介者模式包含以下主要角色:

  • 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。

  • 具体中介者(ConcreteMediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。

  • 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。

  • 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。

1,优点:

  • 松散耦合

    中介者模式通过把多个同事对象之间的交互封装到中介者对象里面,从而使得同事对象之间松散耦合,基本上可以做到互补依赖。这样一来,同事对象就可以独立地变化和复用,而不再像以前那样“牵一处而动全身”了。

  • 集中控制交互

    多个同事对象的交互,被封装在中介者对象里面集中管理,使得这些交互行为发生变化的时候,只需要修改中介者对象就可以了,当然如果是已经做好的系统,那么就扩展中介者对象,而各个同事类不需要做修改。

  • 一对多关联转变为一对一的关联

    没有使用中介者模式的时候,同事对象之间的关系通常是一对多的,引入中介者对象以后,中介者对象和同事对象的关系通常变成双向的一对一,这会让对象的关系更容易理解和实现。

迭代器模式

**定义:**提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。

迭代器模式主要包含以下角色:

  • 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。

  • 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。

  • 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、next() 等方法。

  • 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

迭代器模式在JAVA的很多集合类中被广泛应用,接下来看看JAVA源码中是如何使用迭代器模式的。

List<String> list = new ArrayList<>();
Iterator<String> iterator = list.iterator(); //list.iterator()方法返回的肯定是Iterator接口的子实现类对象
while (iterator.hasNext()) {System.out.println(iterator.next());
}

访问者模式

**定义:**封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。

访问者模式包含以下主要角色:

  • 抽象访问者(Visitor)角色:定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素类个数(Element的实现类个数)是一样的,从这点不难看出,访问者模式要求元素类的个数不能改变。
  • 具体访问者(ConcreteVisitor)角色:给出对每一个元素类访问时所产生的具体行为。
  • 抽象元素(Element)角色:定义了一个接受访问者的方法(accept),其意义是指,每一个元素都要可以被访问者访问。
  • 具体元素(ConcreteElement)角色: 提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
  • 对象结构(Object Structure)角色:定义当中所提到的对象结构,对象结构是一个抽象表述,具体点可以理解为一个具有容器性质或者复合对象特性的类,它会含有一组元素(Element),并且可以迭代这些元素,供访问者访问。

1,分派:

变量被声明时的类型叫做变量的静态类型,有些人又把静态类型叫做明显类型;而变量所引用的对象的真实类型又叫做变量的实际类型。比如 Map map = new HashMap() ,map变量的静态类型是 Map ,实际类型是 HashMap 。根据对象的类型而对方法进行的选择,就是分派(Dispatch),分派(Dispatch)又分为两种,即静态分派和动态分派。

静态分派(Static Dispatch) 发生在编译时期,分派根据静态类型信息发生。静态分派对于我们来说并不陌生,方法重载就是静态分派。

动态分派(Dynamic Dispatch) 发生在运行时期,动态分派动态地置换掉某个方法。Java通过方法的重写支持动态分派。

备忘录模式

备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原,很多软件都提供了撤销(Undo)操作,如 Word、记事本、Photoshop、IDEA等软件在编辑时按 Ctrl+Z 组合键时能撤销当前操作,使文档恢复到之前的状态;还有在 浏览器 中的后退键、数据库事务管理中的回滚操作、玩游戏时的中间结果存档功能、数据库与操作系统的备份操作、棋类游戏中的悔棋功能等都属于这类。

定义:

又叫快照模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。

备忘录模式的主要角色如下:

  • 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
  • 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
  • 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。

备忘录有两个等效的接口:

  • 窄接口:管理者(Caretaker)对象(和其他发起人对象之外的任何对象)看到的是备忘录的窄接口(narror Interface),这个窄接口只允许他把备忘录对象传给其他的对象。
  • 宽接口:与管理者看到的窄接口相反,发起人对象可以看到一个宽接口(wide Interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。

超硬核!!设计模式及设计原则归纳相关推荐

  1. 超硬核!!!一篇文章搞定TCP、UDP、Socket、HTTP(详细网络编程内容+现实解释三次握手四次挥手+代码示例)【网络编程 1】

    TCP.UDP.Socket 一天面试的经验: 什么是网络编程 网络编程中两个主要的问题 网络协议是什么 为什么要对网络协议分层 计算机网络体系结构 1 TCP / UDP 1.1 什么是TCP/IP ...

  2. 十一长假我肝了这本超硬核PDF,现决定开源!!

    写在前面 在 [冰河技术] 微信公众号中的[互联网工程]专题,更新了不少文章,有些读者反馈说,在公众号中刷 历史文章不太方便,有时会忘记自己看到哪一篇了,当打开一篇文章时,似乎之前已经看过了,但就是不 ...

  3. 【Nginx】冰河又一本超硬核Nginx PDF教程免费开源!!

    写在前面 在 [冰河技术] 微信公众号中的[Nginx]专题,更新了不少文章,有些读者反馈说,在公众号中刷 历史文章不太方便,有时会忘记自己看到哪一篇了,当打开一篇文章时,似乎之前已经看过了, 但就是 ...

  4. 超硬核!苏州同程旅游学长给我的全面的面试知识库

    超硬核!苏州同程旅游学长给我的全面的面试知识库 1.简介 新生和经验丰富的C#面试常见问题解答 2.什么是C#? 3.用示例说明C#中的注释类型 4.可以执行多个catch块吗? 5. public, ...

  5. 设计一个矩形类rectangle_万字长文带你捋清六种设计模式的设计原则(建议收藏)...

    对于设计模式,自己很早之前就看了好多本设计模式书籍,其中一些还看了好几遍,也一直希望自己能在编码的时候把这些设计模式用上去.可是,在日常的打码中,用的最多的就是单例,其次是观察者和建造者模式 ( bu ...

  6. mysql long类型_怒肝两个月MySQL源码,我总结出这篇2W字的MySQL协议详解(超硬核干货)!!...

    点击上方蓝色"冰河技术",关注并选择"设为星标" 持之以恒,贵在坚持,每天进步一点点! 作者个人研发的在高并发场景下,提供的简单.稳定.可扩展的延迟消息队列框架 ...

  7. 设计模式 - 七大设计原则(一)

    设计模式 - 七大设计原则(一) 概述 简单介绍一下七大设计原则: 开闭原则:是所有面向对象设计的核心,对扩展开放,对修改关闭 依赖倒置原则:针对接口编程,依赖于抽象而不依赖于具体 单一职责原则:一个 ...

  8. 超硬核之傅里叶公式推导(上)

    超硬核之傅里叶公式推导 1.三角函数系与正交性 2.周期(2pi)函数的傅里叶展开(三角形式) 3.找到周期函数傅里叶展开的系数 结语 前言:再学习数学推导之前,popcorn建议读者感性的先去理解一 ...

  9. 超硬核!兔兔阿里p7学长给的面试知识库

    一个阿里p7学长给的nosql面试知识库,绝对真实,学会了去面呀. 最近整理了一下超硬核系列的文章和面经系列的文章,可以持续关注下: 超硬核系列历史文章:(我保证每篇文章都有值得学习的地方,并且对小白 ...

最新文章

  1. Chart.js-线形图分析(参数分析+例图)
  2. 设计器的使用及常用控件
  3. TOMCAT部署项目的方式
  4. [收藏]编译器内置的一些有用的调试宏
  5. OpenCV3.4.2+VS2015开发环境搭建
  6. mob AndroidStudio 短信SDK集成
  7. kettle连接ClickHouse
  8. QT 读取txt 文件
  9. 摄影用光、构图基础知识
  10. 大一总结与突然的感悟
  11. ios 获取相机胶卷_电影胶片相机的工作原理
  12. 行流 - 通用;仅使用库存接口开单 and 行流 - 通用;
  13. 支付宝集五福最全攻略,五分钟集齐五福!
  14. 计算机网络(三):IP协议,路由器转发,路由器和交换机的区别
  15. 对不起,我现在喜欢划船了,不喜欢爬山了
  16. 国债是什么?国债逆回购是什么?国债逆回购是低风险高回报的投资方式之一
  17. 信息学奥赛初赛题目讲解3
  18. 在线文档编辑工具哪个更好?
  19. matlab电路建模,单相桥式整流电路在MATLAB中的建模与仿真
  20. win10安装账户卡住_win10系统安装时卡死或黑屏的解决方法

热门文章

  1. 过年抢票回家居然掉入诈骗陷阱
  2. RiPro主题高端美化设计素材软件下载站子主题
  3. springmvc同时配置html和jsp视图解析器
  4. 马尔可夫过程与泊松过程
  5. 代理IP技术的介绍及应用指南
  6. c语言下载函数,C语言的下载函数
  7. html中选择器eq,jQuery 选择器之eq是什么意思
  8. Simple Math Problem
  9. JMAIL 80040154 错误
  10. 《CTF特训营》——利用特性实现的攻击