本文旨在快速梳理经常使用的设计模式,了解每一个模式主要针对的是哪些状况以及其基础特征,每一个模式前都有列举出一个或多个能够深刻阅读的参考网页,以供读者详细了解其实现。html
分为三篇文章:java
经过如下两种途径查看全复习手册文章导航git
结构型程序员
首先搞清楚一点,设计模式不是高深技术,不是奇淫技巧。设计模式只是一种设计思想,针对不一样的业务场景,用不一样的方式去设计代码结构,其最最本质的目的是为了解耦,延伸一点的话,还有为了可扩展性和健壮性,可是这都是创建在解耦的基础之上。github
高内聚:系统中A、B两个模块进行交互,若是修改了A模块,不影响模块B的工做,那么认为A有足够的内聚。面试
低耦合:就是A模块与B模块存在依赖关系,那么当B发生改变时,A模块仍然能够正常工做,那么就认为A与B是低耦合的。算法
https://www.jianshu.com/p/93821721bf08apache
客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。所以,适配器可使因为接口不兼容而不能交互的类能够一块儿工做。这就是适配器模式的模式动机。编程
目标(Target)角色:这就是所期待获得的接口。注意:因为这里讨论的是类适配器模式,所以目标不能够是类。设计模式
源(Adapee)角色:如今须要适配的接口。
适配器(Adaper)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不能够是接口,而必须是具体类。
建立新类,继承源类,并实现新接口
class adapter extends oldClass implements newFunc{}
复制代码
建立新类持源类的实例,并实现新接口
class adapter implements newFunc { private oldClass oldInstance ;}
复制代码
类适配器使用对象继承的方式,是静态的定义方式
而对象适配器使用对象组合的方式,是动态组合的方式。
建立新的抽象类实现旧接口方法
abstract class adapter implements oldClassFunc { void newFunc();}
复制代码
建议尽可能使用对象适配器的实现方式,多用合成/聚合、少用继承。固然,具体问题具体分析,根据须要来选用实现方式,最适合的才是最好的。
过多的使用适配器,会让系统很是零乱,不易总体进行把握。好比,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统若是太多出现这种状况,无异于一场灾难。所以若是不是颇有必要,能够不使用适配器,而是直接对系统进行重构。
给一类对象增长新的功能,装饰方法与具体的内部逻辑无关。
设计不一样种类的饮料,饮料能够添加配料,好比能够添加牛奶,而且支持动态添加新配料。每增长一种配料,该饮料的价格就会增长,要求计算一种饮料的价格。
下图表示在 DarkRoast 饮料上新增新添加 Mocha 配料,以后又添加了 Whip 配料。DarkRoast 被 Mocha 包裹,Mocha 又被 Whip 包裹。它们都继承自相同父类,都有 cost() 方法,外层类的 cost() 方法调用了内层类的 cost() 方法。
interface Source{ void method();}
public class Decorator implements Source{
private Source source ;
public void decotate1(){
System.out.println("decorate");
}
@Override
public void method() {
decotate1();
source.method();
}
}
复制代码
装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。
用代理模式,代理类(proxy class)能够对它的客户隐藏一个对象的具体信息。所以,当使用代理模式的时候,咱们经常在一个代理类中建立一个对象的实例。
当咱们使用装饰器模 式的时候,咱们一般的作法是将原始对象做为一个参数传给装饰者的构造器。
详细代码实例:https://www.cnblogs.com/daniels/p/8242592.html
代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
通俗的来说代理模式就是咱们生活中常见的中介。
中介隔离做用
在某些状况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象能够在客户类和委托对象之间起到中介的做用,其特征是代理类和委托类实现相同的接口。
开闭原则,增长功能
真正的业务功能仍是由委托类来实现,可是能够在业务功能执行的先后加入一些公共的服务。例如咱们想给项目加入缓存、日志这些功能,咱们就可使用代理类来完成,而不必打开已经封装好的委托类。
是由程序员建立或特定工具自动生成源代码,在对其编译。在程序员运行以前,代理类.class文件就已经被建立了。
代码:静态代理建立代理类:
package main.java.proxy.impl;
import main.java.proxy.BuyHouse;
public class BuyHouseProxy implements BuyHouse {
private BuyHouse buyHouse;
public BuyHouseProxy(final BuyHouse buyHouse) {
this.buyHouse = buyHouse;
}
@Override
public void buyHosue() {
System.out.println("买房前准备");
buyHouse.buyHosue();
System.out.println("买房后装修");
}
}
复制代码
静态代理总结
优势:能够作到在符合开闭原则的状况下对目标对象进行功能扩展。
缺点:咱们得为每个服务都得建立代理类,工做量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。
是在程序运行时经过反射机制动态建立的。
为须要拦截的接口生成代理对象以实现接口方法拦截功能。
代码:编写动态处理器
package main.java.proxy.impl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(final Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("买房前准备");
Object result = method.invoke(object, args);
System.out.println("买房后装修");
return result;
}
}
复制代码
代码:编写测试类
package main.java.proxy.test;
import main.java.proxy.BuyHouse;
import main.java.proxy.impl.BuyHouseImpl;
import main.java.proxy.impl.DynamicProxyHandler;
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new
Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse));
proxyBuyHouse.buyHosue();
}
}
复制代码
动态代理总结
优点:虽然相对于静态代理,动态代理大大减小了咱们的开发任务,同时减小了对业务接口的依赖,下降了耦合度。
劣势:只能对接口进行代理
其原理是经过字节码技术为一个类建立子类,并在子类中采用方法拦截的技术拦截全部父类方法的调用,顺势织入横切逻辑。
但由于采用的是继承,因此不能对final修饰的类进行代理。
JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
代码见网页
CGLIB代理总结:(与JDK代理区别:)
CGLIB建立的动态代理对象比JDK建立的动态代理对象的性能更高,可是CGLIB建立代理对象时所花费的时间却比JDK多得多。
https://www.cnblogs.com/lthIU/p/5860607.html
简单来讲,该模式就是把一些复杂的流程封装成一个接口供给外部用户更简单的使用。这个模式中,涉及到3个角色。
1)门面角色:外观模式的核心。它被客户角色调用,它熟悉子系统的功能。内部根据客户角色的需求预约了几种功能的组合。
2)子系统角色: 实现了子系统的功能。它对客户角色和Facade时未知的。它内部能够有系统内的相互交互,也能够由供外界调用的接口。
3)客户角色: 经过调用Facede来完成要实现的功能。
package com.huawei.facadeDesign.facade;
import org.apache.log4j.Logger;
import com.huawei.facadeDesign.children.CPU;
import com.huawei.facadeDesign.children.Disk;
import com.huawei.facadeDesign.children.Memory;
/**
* 门面类(核心)
* @author Administrator
*
*/
public class Computer
{
public static final Logger LOGGER = Logger.getLogger(Computer.class);
private CPU cpu;
private Memory memory;
private Disk disk;
public Computer()
{
cpu = new CPU();
memory = new Memory();
disk = new Disk();
}
public void start()
{
LOGGER.info("Computer start begin");
cpu.start();
disk.start();
memory.start();
LOGGER.info("Computer start end");
}
public void shutDown()
{
LOGGER.info("Computer shutDown begin");
cpu.shutDown();
disk.shutDown();
memory.shutDown();
LOGGER.info("Computer shutDown end...");
}
}
复制代码
松散耦合:使得客户端和子系统之间解耦,让子系统内部的模块功能更容易扩展和维护;
简单易用:客户端根本不须要知道子系统内部的实现,或者根本不须要知道子系统内部的构成,它只须要跟Facade类交互便可。
更好的划分访问层次:有些方法是对系统外的,有些方法是系统内部相互交互的使用的。子系统把那些暴露给外部的功能集中到门面中,这样就能够实现客户端的使用,很好的隐藏了子系统内部的细节。
http://www.cnblogs.com/houleixx/archive/2008/02/23/1078877.html
在软件系统中,某些类型因为自身的逻辑,它具备两个或多个维度的变化,那么如何应对这种“多维度的变化”?
如何利用面向对象的技术来使得该类型可以轻松的沿着多个方向进行变化,而又不引入额外的复杂度?这就要使用Bridge模式。
由上图变为下图
详细代码见参考网页
static void Main(string[] args){
//男人开着公共汽车在高速公路上行驶;
Console.WriteLine("=========================");
AbstractRoad Road3 = new SpeedWay();
Road3.Car = new Bus();
people p = new Man();
p.Road = Road3;
p.Run();
Console.Read();
}
复制代码
https://www.cnblogs.com/lfxiao/p/6816026.html
组合模式是为了表示那些层次结构,同时部分和总体也多是同样的结构,常见的如文件夹或者树.
组合模式定义了如何将容器对象和叶子对象进行递归组合,使得客户在使用的过程当中无须进行区分,能够对他们进行一致的处理。
在使用组合模式中须要注意一点也是组合模式最关键的地方:叶子对象和组合对象实现相同的接口。这就是组合模式可以将叶子节点和对象节点进行一致处理的缘由。
1.Component :组合中的对象声明接口,在适当的状况下,实现全部类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。
2.Leaf:叶子对象。叶子结点没有子结点。
3.Composite:容器对象,定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操做,如增长(add)和删除(remove)等。
一、须要表示一个对象总体或部分层次,在具备总体和部分的层次结构中,但愿经过一种方式忽略总体与部分的差别,能够一致地对待它们。
二、让客户可以忽略不一样对象层次的变化,客户端能够针对抽象构件编程,无须关心对象层次结构的细节。
https://www.cnblogs.com/chenssy/p/3330555.html
所谓享元模式就是运行共享技术有效地支持大量细粒度对象的复用,因此享元模式要求可以共享的对象必须是细粒度对象。
内部状态:在享元对象内部不随外界环境改变而改变的共享部分。
外部状态:随着环境的改变而改变,不可以共享的状态就是外部状态。
享元工厂类FlyweightFactory:
利用了HashMap保存已经建立的颜色
public class FlyweightFactory{
static Map<String, Shape> shapes = new HashMap<String, Shape>();
public static Shape getShape(String key){
Shape shape = shapes.get(key);
//若是shape==null,表示不存在,则新建,而且保持到共享池中
if(shape == null){
shape = new Circle(key);
shapes.put(key, shape);
}
return shape;
}
public static int getSum(){
return shapes.size();
}
}
复制代码
客户端程序:Client.java
public class Client {
public static void main(String[] args) {
Shape shape1 = FlyweightFactory.getShape("红色");
shape1.draw();
Shape shape2 = FlyweightFactory.getShape("灰色");
shape2.draw();
Shape shape3 = FlyweightFactory.getShape("绿色");
shape3.draw();
Shape shape4 = FlyweightFactory.getShape("红色");
shape4.draw();
Shape shape5 = FlyweightFactory.getShape("灰色");
shape5.draw();
Shape shape6 = FlyweightFactory.getShape("灰色");
shape6.draw();
System.out.println("一共绘制了"+FlyweightFactory.getSum()+"中颜色的圆形");
}
}
复制代码
简书大牛:https://www.jianshu.com/nb/10186551
Github:https://github.com/CyC2018/Interview-Notebook/blob/master/notes/设计模式.md
菜鸟网:http://www.runoob.com/design-pattern/design-pattern-tutorial.html
补充:
23种设计模式总结:
https://www.cnblogs.com/tongkey/p/7170826.html
https://www.cnblogs.com/malihe/p/6891920.html
更多精彩文章,请查阅个人博客或关注个人公众号:Rude3Knife
全复习手册文章导航
知识点复习手册文章推荐
我是蛮三刀把刀,目前为后台开发工程师。主要关注后台开发,网络安全,Python爬虫等技术。
来微信和我聊聊:yangzd1102
Github:https://github.com/qqxx6661
同步更新如下博客
1. Csdn
拥有专栏:Leetcode题解(Java/Python)、Python爬虫开发、面试助攻手册
2. 知乎
拥有专栏:码农面试助攻手册
3. 掘金
4. 简书
若是文章对你有帮助,不妨收藏起来并转发给您的朋友们~