ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

23种设计模式

2021-04-08 16:01:05  阅读:98  来源: 互联网

标签:23 void System public println 设计模式 class out


创建型模式

单例模式,工厂模式,抽象工厂模式,建造者模式,原型模式。

结构型模式

适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式。

行为型模式

模版模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,职责链模式,访问者模式。

单例模式

核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

常用应用场景:

  • Windowsd 的Task Managed(任务管理器)就是一个典型的单例模式
  • Windows的Recycle Bina(回收站)也是一个典型的单例模式。在整个系统的运行过程中,回收站一直维持着仅有的一个实例。
  • 项目中,读取配置文件的类,一般也只有一个对象,没必要每次使用配置文件数据,每次new一个对象去读取。
  • 网站的计数器,一般也是采用单例模式实现,否则难以同步。
  • 应用程序的日志应用,一般都可以采用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能一个实例去操作,否则不好追加。
  • 数据库连接池的设计一般也是采用单例模式,因为数据库的连接是一种数据库资源。
  • 操作系统的文件系统,也是大的单例系统的具体例子,一个操作系统只能有一个文件系统。
  • Application也是单例的典型应用(Servelet编程中也会涉及)
  • 在Spring中,每个Bean默认就是单例的,这样做的优点是Spring容器可以管理。
  • 在servelet编程中,每个servelet也是单例。
  • 在SpringMVC框架/struts1框架中,控制器对象也是单例。

单例模式的优点:

  • 由于单例模式只生成一个实例,减少了系统性的消耗,当以一个对象产生的需要比较多的资源时,如读取配置,产生其它依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
  • 单例模式可以在系统设置全局的访问点,优化共享资源访问,例如可以设计一个单例类,负责手游数据表的映射处理。

常见的五种单例模式实现方式

  • 主要

    饿汉式(线程安全,调用效率高,但是不能延时加载)

    懒汉式(线程安全,调用效率不高,但是可以延时加载)

  • 其它

    双重检测锁式(由于JVM底层内部模型原因,偶尔会出现问题,不建议使用)

    静态内部类式(线程安全,调用效率高,但是可以延时加载)

    枚举单例(线程安全,调用效率高,不能延时加载)

饿汉模式

//单例模式的饿汉模式
public class SingletonDemo {
    //类初始化时,立即加载(没有延时加载的优势)
    private static SingletonDemo singletonDemo=new SingletonDemo();

    private SingletonDemo(){}

    //方法没有同步,调用效率高
    public static SingletonDemo getInstance(){
        return singletonDemo;
    }
}

饿汉模式中,static变量在类加载是初始化,此时不会涉及多个线程的对象访问对象的问题,虚拟机保证只装载一次该类,肯定不会发生并发访问的问题。因此,可以省略synchronized关键字。

如果知识加载本类,而不是调用getInstance()甚至永远没有调用,则会造成资源浪费。

懒汉模式

//单例模式的懒汉模式
class lazySingletonDemo{
    private static lazySingletonDemo lazySingletonDemo;

    private lazySingletonDemo(){}

    public static synchronized lazySingletonDemo getInstance(){
        if (lazySingletonDemo==null){
            lazySingletonDemo=new lazySingletonDemo();
        }
        return lazySingletonDemo;
    }
}

要点:

lazy load 延迟加载,懒加载,真正用的时候才加载

问题:

资源利用率高了,但是,每次点调用getInstance()方法都要同步,并发效率较低。

双重检测锁模式

//双重检测锁
class doubleCheckSingletonDemo{
    private static doubleCheckSingletonDemo doubleCheckSingletonDemo=null;

    private doubleCheckSingletonDemo(){}

    public static doubleCheckSingletonDemo getInstance(){
        if(doubleCheckSingletonDemo==null){
            doubleCheckSingletonDemo d;
            synchronized (doubleCheckSingletonDemo.class){
                d=doubleCheckSingletonDemo;
                if(d==null){
                    synchronized (doubleCheckSingletonDemo.class){
                        if(d==null){
                            d=new doubleCheckSingletonDemo();
                        }
                    }
                }
                doubleCheckSingletonDemo=d;
            }
        }
        return doubleCheckSingletonDemo;
    }
}

设个模式将同步内容下放到if,提高执行效率,不必每次获取对象都进行同步,只有第一次才同步以后就没必要了。

问题:由于编译器优化原因和JVM的内部模型的原因,偶尔会出问题,不建议使用。

静态内部实现

//静态内部实现
class staticSingletonDemo{

    private static class staticSingletonInstance{
        private static final staticSingletonDemo STATIC_SINGLETON_DEMO=new staticSingletonDemo();
    }

    public static staticSingletonDemo getInstance(){
        return staticSingletonInstance.STATIC_SINGLETON_DEMO;   
    }

    private staticSingletonDemo(){}
}

要点:

  • 外部类没有static属性,不会像饿汉式那样立即加载对象。
  • 只有真正调用getInstance(),才会加载静态内部类。加载类是线程安全的。staticSingletonDemo是static final类型的,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了此线程安全
  • 兼备了并发高效调用和延迟加载的优势。

枚举模式

//枚举的单例模式
public enum enumSingletonDemo {

    //这个枚举元素,代表一个单例对象
    INSTANCE;

    //添加自己需要的操作     
    public void SingleDemoOpreation(){

    }
}

优点:

  • 实现简单。
  • 枚举本身就是单例模式,由JVM从根本上提供保障!避免通过反射和反序列化的漏洞。

缺点:

  • 无加载延时。

单例模式测试:

public static void main(String[] args) {
        SingletonDemo s1=SingletonDemo.getInstance();
        SingletonDemo s2=SingletonDemo.getInstance();
        System.out.println(s1);
        System.out.println(s2);
    }

//输出结果
//Algorithm.DesignPattern.SingletonDemo@1d81eb93
//Algorithm.DesignPattern.SingletonDemo@1d81eb93

反射和反序列化可以破解单例模式

反射破解
 Class<SingletonDemo> cls = (Class<SingletonDemo>)Class.forName("Algorithm.DesignPattern.SingletonDemo");
        Constructor<SingletonDemo> c=cls.getDeclaredConstructor(null);
        SingletonDemo s3=c.newInstance();
        SingletonDemo s4=c.newInstance();
        System.out.println(s3);
        System.out.println(s4);
//结果展示
//Algorithm.DesignPattern.SingletonDemo@7291c18f
//Algorithm.DesignPattern.SingletonDemo@34a245ab

防止反射破解单例模式的方法

在构造器中判断实例是否为空,如果不为空就抛出异常。

private SingletonDemo(){
        if (singletonDemo!=null){
            throw new RuntimeException();
        }
    }
反序列化破解
SingletonDemo s1=SingletonDemo.getInstance();
SingletonDemo s2=SingletonDemo.getInstance();
System.out.println(s1);
System.out.println(s2);
FileOutputStream fos=new FileOutputStream("d.txt");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(s1);
oos.close();
fos.close();

ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d.txt"));
ois.close();
SingletonDemo s3=(SingletonDemo) ois.readObject();
System.out.println(s3);
//Algorithm.DesignPattern.SingletonDemo@1d81eb93
//Algorithm.DesignPattern.SingletonDemo@1d81eb93
//Algorithm.DesignPattern.SingletonDemo@6d21714c

反序列化破解解决方式

添加一个方法

//在反反序列化时,如果定义了readResolve函数,直接返回此方法指定对象,不需要在单独创建对象。
public Object readResolve(){
	return singletonDemo;
}

工厂模式

工厂模式:

实现了调用者和创建者的分离。

详细分类:

  • 简单工厂
  • 工厂方法模式
  • 抽象工厂模式

面向对象设计的基本原则

  • OCP(开闭原则) 一个软件的实体应当对扩展开放,对修改关闭。
  • DIP(依赖倒转原则)要针对接口编程,不要针对实现编程。
  • LoD(迪米特原则)只与你的直接朋友通信,避免和陌生人通信。

简单工厂模式

实例

public class client {
    public static void main(String[] args) {
        car c1=new simplefactory().createa("奥迪");
        car c2=new simplefactory().createa("比亚迪");
        c1.run();
        c2.run();
    }
}
//定义一个car的接口
interface car {
    public void run();
}

//奥迪车实现接口
class Audi implements car {
    @Override
    public void run() {
        System.out.println("奥迪在跑");
    }
}
//比亚迪车实现接口
class Byd implements car {
    @Override
    public void run() {
        System.out.println("比亚迪在跑");
    }
}
//创建工厂类实现类的产生
class simplefactory {
    public car create(String type){
        if ("奥迪".equals(type)){
            return new Audi();
        }else if("比亚迪".equals(type)){
            return new Byd();
        }else {
            return null;
        }
    }
}

要点:

  • 简单工厂模式也叫静态工厂模式,就是工厂类一般是静态方法,通过接受参数的不同来返回不同的对象实例。
  • 对于新增加的产品无能为力!不修改代码的话,无法扩展。

工厂方法模式

要点

  • 为了避免简单工厂模式的缺点,不完全满足OCP。
  • 工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式只有一个(对于项目或者模块来说)工厂类,而工厂方法模式有一组实现了相同接口的工厂类。

示例

public class client {
    public static void main(String[] args) {
        car c1=new audiaFactory().createCar();
        car c2=new bydFactory().createCar();
        c1.run();
        c2.run();
    }
}
public interface carFactory {
    car createCar();
}
public class audiaFactory implements carFactory {
    @Override
    public car createCar() {
        return new Audi();
    }
}

public class bydFactory implements carFactory {
    @Override
    public car createCar() {
        return new Byd();
    }
}

简单工厂和工厂方法模式发PK

  • 结构复杂度

    从这个角度出发,显然简单工厂模式要占优,简单工厂只需要一个工厂类,而工厂方法模式类随着产品类的个数增加而增加,这无疑会使类的个数越来越多,从而增加了结构的复杂度。

  • 代码复杂度

    代码复制到和结构复杂度是一对矛盾,既然简单工厂在结构方面相对简洁,那么它在代码方面肯定是比工厂方法模式复杂了。简单工厂模式的工厂类随着产品类的增加需要增加很多的方法或代码,而工厂模式每个具体的工厂类只完成单一任务,代码简洁。

  • 客服端编程难度

    工厂方法模式虽然在工厂类结构中引入了接口而满足了OCP,但是在客服端编码中需要对工厂类进行初始化,而简单模式的工厂类是一个静态类,在客户端不需要实例化,这无疑是一个吸引人的优点。

  • 管理上的难度

    这是一个关键的问题。

    我们先谈扩展,众所周知,工厂方法模式完全满足OCP,即它有分常良好的扩展性。那是否就说明了 简单工厂模式就没有扩展性呢,答案是否对的,简单工厂模式同样具有良好的扩展性-扩展的时候需要少量的代码(修改过程类的代码)就可以满足扩展性的要求了,尽管这没有完全满足OCP,但我们不需要太拘泥于设计理论。

    然后我们从维护性的角度分析,假如某个具体的产品类需要进行一定的修改,很有可能需要修改对应的工厂类。当同时需要修改多个产品类的时候,对工厂类的修改会变得非常麻烦,反而简单工厂没有这些麻烦,当多个产品类需要修改的时候,简单工厂模式任然仅仅需要修改唯一的工厂类。

    根据设计理论建议:工厂方法模式,但实际上,我们一般都使用简单工厂模式。

抽象工厂模式

  • 用来生产不同产品族发全部产品(对于增加新的产品,无能为力;支持增加产品族)
  • 抽象工厂模式是工厂方法模式发升级版本,在有多个业务品种,业务分类是,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。

抽象工厂示例

public interface Engine {
    void run();
    void start();
}

class LuxuryEngine implements Engine{
    @Override
    public void run() {
        System.out.println("高端发动机跑得快");
    }

    @Override
    public void start() {
        System.out.println("高端发动机启动快");
    }
}

class LowEngine implements Engine{
    @Override
    public void run() {
        System.out.println("低端发动机跑得慢");
    }

    @Override
    public void start() {
        System.out.println("低端发动机启动慢");
    }
}

public interface Seat {
    void message();
}
class LuxurySeat implements Seat{
    @Override
    public void message() {
        System.out.println("高端座椅能按摩,很舒服");
    }
}
class LowSeat implements Seat{
    @Override
    public void message() {
        System.out.println("低端座椅不能按摩");
    }
}

public interface Tyre {
    void revole();
}
class LuxuryTyre implements Tyre{
    @Override
    public void revole() {
        System.out.println("高端轮胎转动不磨损");
    }
}

class LowTyre implements Tyre{
    @Override
    public void revole() {
        System.out.println("低端轮胎转动要磨损");
    }
}

public interface CarFactory {
    Engine createEngine();
    Seat createSeat();
    Tyre creatTyre();
}

class LuxuryCarFactory implements CarFactory{
    @Override
    public Engine createEngine() {
        return new LuxuryEngine();
    }

    @Override
    public Seat createSeat() {
        return new LuxurySeat();
    }

    @Override
    public Tyre creatTyre() {
        return new  LuxuryTyre();
    }
}

class LowCarFactory implements CarFactory{
    @Override
    public Engine createEngine() {
        return new LowEngine();
    }

    @Override
    public Seat createSeat() {
        return new LowSeat();
    }

    @Override
    public Tyre creatTyre() {
        return new  LowTyre();
    }
}

public class client {
    public static void main(String[] args) {
        CarFactory factory=new LuxuryCarFactory();
        Engine e=factory.createEngine();
        e.run();
        e.start();
    }
}

应用场景:

  • JDK中Calendar的getInstance方法。
  • JDBC中Connection对象的获取。
  • HIbernate中SessionFactory创建Session。
  • spring中IOC容器创建管理bean对象。
  • XML解析是DocumentBuilderFactory创建解析器对象。
  • 反射中Class对象的newInstance()

建造者模式

分离了对像子组件的单独构造(有builder来负责)和装配(由Director负责)。从而构造出复杂的对象。这个模式适用于:某个对象的构建过程复杂的情况下使用。

由于实现了构建和装配的解耦,不同的构建器,相同的装配,也可以做出不同的对象,相同的构建器,不同的装配顺序也可以做出不同的对象。也就是构建算法,装配算法的解耦,实现更好的复用。

//火箭实体类
public class Airship {
    private OrbitalMOudel orbitalMOudel; //轨道仓
    private Engine engine; //发动机
    private EscapeTower escapeTower;  //逃逸塔

    public OrbitalMOudel getOrbitalMOudel() {
        return orbitalMOudel;
    }

    public void setOrbitalMOudel(OrbitalMOudel orbitalMOudel) {
        this.orbitalMOudel = orbitalMOudel;
    }

    public Engine getEngine() {
        return engine;
    }

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public EscapeTower getEscapeTower() {
        return escapeTower;
    }

    public void setEscapeTower(EscapeTower escapeTower) {
        this.escapeTower = escapeTower;
    }
}

class EscapeTower{
    private String escapeTower;

    public String getEscapeTower() {
        return escapeTower;
    }

    public void setEscapeTower(String escapeTower) {
        this.escapeTower = escapeTower;
    }

    public EscapeTower(String escapeTower) {
        this.escapeTower = escapeTower;
    }
}
class Engine{
    private String engine;

    public String getEngine() {
        return engine;
    }

    public void setEngine(String engine) {
        this.engine = engine;
    }

    public Engine(String engine) {
        this.engine = engine;
    }
}

class OrbitalMOudel{
    private String orbitalMOudel;

    public OrbitalMOudel(String orbitalMOudel) {
        this.orbitalMOudel = orbitalMOudel;
    }

    public String getOrbitalMOudel() {
        return orbitalMOudel;
    }

    public void setOrbitalMOudel(String orbitalMOudel) {
        this.orbitalMOudel = orbitalMOudel;
    }
}
//构造器接口
public interface AirShipBuilder {
    OrbitalMOudel builderOrbitalMOudel();
    Engine builderEngine();
    EscapeTower builderEscapeTower();
}

//组装器接口
public interface AirShipDirector {
    Airship directAirShip(); // 组装飞船
}

//构造器实现类
public class SzAirShipBuilder implements AirShipBuilder {

    @Override
    public OrbitalMOudel builderOrbitalMOudel() {
        System.out.println("构建神州飞船的轨道仓");
        return new OrbitalMOudel("神州轨道仓");
    }

    @Override
    public Engine builderEngine() {
        System.out.println("构建神州飞船的发动机");
        return new Engine("神州发动机");
    }

    @Override
    public EscapeTower builderEscapeTower() {
        System.out.println("构建神州飞船的逃逸塔");
        return new EscapeTower("神州逃逸塔");
    }
}

//组装器实现类
public class SzShipDireactor implements AirShipDirector{

    private AirShipBuilder airShipBuilder;

    public SzShipDireactor(AirShipBuilder airShipBuilder) {
        this.airShipBuilder = airShipBuilder;
    }

    @Override
    public Airship directAirShip() {
        //组装飞船
        Engine engine=airShipBuilder.builderEngine();
        EscapeTower escapeTower= airShipBuilder.builderEscapeTower();
        OrbitalMOudel orbitalMOudel= airShipBuilder.builderOrbitalMOudel();
        Airship airship=new Airship();
        airship.setEngine(engine);
        airship.setEscapeTower(escapeTower);
        airship.setOrbitalMOudel(orbitalMOudel);
        return airship;
    }
}

测试

public class client {
    public static void main(String[] args) {
        AirShipDirector airShipDirector=new SzShipDireactor(new SzAirShipBuilder());
        Airship airship=airShipDirector.directAirShip();
    }
}

结果:

构建神州飞船的发动机
构建神州飞船的逃逸塔
构建神州飞船的轨道仓

开发应用场景

  • Stringbuilder中的append方法
  • SQL中的PreparedStatement
  • JDOM中,DomBuilder,SAXBuilder

原型模式prototype

场景:

  • 思考一下,克隆技术是怎样的过程?
  • javascript语言中,继承是怎么实现的,那里面也有prototype。

原型模式:

  • 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
  • 就是java中的克隆技术,以某个对象为原型,复制出新的对象。显然,新的对象具备原型对象的特点
  • 优势有:效率高(直接克隆,避免了重新执行构造过程步骤)。
  • 克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出的对象的属性值完全和原型对象相同。并且克隆出的新对象改变不会影响原型对象。然后,再修改克隆对象的值。

原型模式实现

  • Cloneable接口和clone方法
  • Prototype模式中实现起来最困难的地方就是内存复制操作,所幸在Java中提供了clone(方法替我们做了绝大部分事情。
//创建一个羊的实例来展示原型模式
public class Sheep implements Cloneable{
    private String name;
    private Date date;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public Sheep() {
    }

    public Sheep(String name, Date date) {
        this.name = name;
        this.date = date;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
}

测试

public class client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep s1=new Sheep("多莉",new Date(321132L));
        System.out.println(s1);
        System.out.println(s1.getName()+" "+s1.getDate());
        Sheep s2= (Sheep) s1.clone();
        System.out.println(s2);
        System.out.println(s2.getName()+" "+s2.getDate());
    }
}

输出结果:

Algorithm.DesignPattern.prototype.Sheep@7291c18f
多莉 Thu Jan 01 08:05:21 CST 1970
Algorithm.DesignPattern.prototype.Sheep@2e817b38
多莉 Thu Jan 01 08:05:21 CST 1970


这种模式成为浅克隆,因为这种克隆方式,中间的Date对象没有改变,两个对象s1,s2使用的同一个Date对象,存在耦合。

可以通过改变代码实现深克隆,即每一个属性同时通过克隆得到的,修改实体类的clone方法如下。

@Override
    protected Object clone() throws CloneNotSupportedException {
//        实现深克隆
        Object obj=super.clone();
        Sheep s= (Sheep) obj;
        s.date= (Date) this.date.clone();
        return obj;
    }

利用序列化和反序列化实现实现深克隆

public class client {
    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Sheep s1=new Sheep("多莉",new Date(321132L));
        System.out.println(s1);
        System.out.println(s1.getName()+" "+s1.getDate());

        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(bos);
        oos.writeObject(s1);
        byte[]bytes=bos.toByteArray();
        ByteArrayInputStream bis=new ByteArrayInputStream(bytes);
        ObjectInputStream ois=new ObjectInputStream(bis);
        Sheep s2= (Sheep) ois.readObject();
        System.out.println(s2);
        System.out.println(s2.getName()+" "+s2.getDate());
    }
}

应用场景

  • 原型模式很少单独出现,一般是和工厂模式一起出现,通过clone的方法创建一个对象,然后又工厂方法提供给调用者
    • spring中Bean的创建实际就是两种:单例模式和原型模式。(当然,原型模式需要和工厂模式搭配起来).

设配器模式

什么是适配器模式?

  • 将一个类的接口转换成客户希望的另一个接口,Adapter模式使得原本不兼容而不能一起工作的那些类可以在一起工作。

模式中的角色

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

代码示例

//被适配类
public class Adaptee {
    public void request(){
        System.out.println("可以完成客户的所有需求!");
    }
}

public interface Target {
    public void handleReq();
}

//适配器本身
//这种方式称为类适配器
public class Adapter extends Adaptee implements Target
{
    @Override
    public void handleReq() {
        super.request();
    }
}

对象适配器

public class Adapter implements Target{

    private Adaptee adaptee;

    public Adaptee getAdaptee() {
        return adaptee;
    }

    @Override
    public void handleReq() {
        this.adaptee.request();
    }
}

使用场景

  • 经常用来做旧系统的改造和升级。
  • 如果我们的系统该法之后再也不需要维护,很多的模式都没有必要的,但是不幸的是,事实上维护一个系统的代价往往是开发一个系统的数倍。

适配器模式的优缺点

  • 优点
    • 更好的复用性:系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
    • 更好的扩展性:在实现适配器功能的时候,可以扩展自己源的行为(增加方法),从而自然地扩展系统的功能。
  • 缺点
    • 会导致系统紊乱:滥用适配器,会让系统变得非常零乱。例如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。

代理模式

  • 核心作用

    通过代理,控制对象的访问!

    可以详细的控制访问某个对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。

  • AOP(Aspect Oriented Programming面向切面编程)的核心实现机制 。

  • 核心角色

    • 抽象角色

      定义代理角色和真实角色的公共对外方法。

    • 真实角色

      实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

      关注真正的业务逻辑。

    • 代理角色

      实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽角色,并可以附加自己的操作。

      将统一的流程控制放到代理角色中处理。

  • 应用场景

    • 安全代理:屏蔽岁真实角色的直接访问。
    • 远程代理:通过代理类处理远程方法的调用。
    • 延迟加载:先加载轻量级的代理方法,真正需要在加载真实对象。
  • 分类

    • 静态代理(静态定义代理类)
    • 动态代理(动态生产代理类)
      • JDK自带的动态代理
      • Javaassist字节码操作库的实现
      • CGLIB
      • ASM(底层使用指令,可维护性差)

静态代理示例

public interface Star {
    void confer();//面谈
    void singContract();//签合同
    void bookTicket();//订票
    void sing();//唱歌
    void collectMoney();//收钱
}

代理类

public class ProxyStar implements Star {

    private Star star;

    public ProxyStar(Star star) {
        this.star = star;
    }

    @Override
    public void confer() {
        System.out.println("代理面谈");
    }
    @Override
    public void singContract() {
        System.out.println("代理签合同");
    }
    @Override
    public void bookTicket() {
        System.out.println("代理订票");
    }
    @Override
    public void sing() {
        star.sing();
    }
    @Override
    public void collectMoney() {
        System.out.println("代理收钱");
    }
}

真实类

public class RealStar implements Star {
    @Override
    public void confer() {
        System.out.println("真角色面谈");
    }

    @Override
    public void singContract() {
        System.out.println("真角色签合同");
    }

    @Override
    public void bookTicket() {
        System.out.println("真角色订票");
    }

    @Override
    public void sing() {
        System.out.println("真角色(周杰伦)唱歌");
    }

    @Override
    public void collectMoney() {
        System.out.println("真角色收钱");
    }
}

测试

public class client {
    public static void main(String[] args) {
        Star real=new RealStar();
        Star proxyStar=new ProxyStar(real);
        proxyStar.confer();
        proxyStar.bookTicket();
        proxyStar.sing();
    }
}
//结果
//代理面谈
//代理订票
//真角色(周杰伦)唱歌

动态代理

JDK自带的动态代理

  • Java.lang.reflect.Proxy
    • 作用:生成代理类和对象
  • java.lang.reflect.InvocationHandler(处理器接口)
    • 可以通过invoke的方法实现对真实角色的代理访问
    • 每次通过Proxy生成代理类对象时都要指定对应的处理器对象。

动态代理示例

public class StarHandler implements InvocationHandler {
    Star star;
    public StarHandler(Star star) {
        this.star = star;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        method.invoke(star,args);
        return null;
    }
}
public class client {
    public static void main(String[] args) {
        Star realstar =new RealStar();
        StarHandler starHandler=new StarHandler(realstar);

        Star proxyStar = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Star.class}, starHandler);

        proxyStar.bookTicket();
        proxyStar.sing();

    }
}

桥接模式

场景

  • 商城系统中常见的商品分类,以电脑为例,如何良好的处理商品分类销售的问题?

问题

  • 扩展性问题(类个数膨胀问题)
    • 如果要增加一个新的电脑类型:智能手机,则要增加各个平台下面的类。
    • 如果要增加一个品牌,也要增加各种电脑类型的类。
  • 违反单一职责原则:
    • 一个类:联想笔记本,有两个引起这个类变化的原因。

桥接模式核心要点

  • 处理多继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以杜拉拉的扩展在抽象层建立关联。

不使用桥接模式的示例

public interface Computer {
    void sale();
}

class DeskTop implements Computer{

    @Override
    public void sale() {
        System.out.println("销售台式机");
    }
}
class Laptop implements Computer{
    @Override
    public void sale() {
        System.out.println("销售笔记本");
    }
}
class Paid implements Computer{
    @Override
    public void sale() {
        System.out.println("销售平板电脑");
    }
}

class LenovoDesktop extends DeskTop{
    @Override
    public void sale() {
        System.out.println("销售联想台式机");
    }
}
class LenovoLaptop extends Laptop{
    @Override
    public void sale() {
        System.out.println("销售联想笔记本");
    }
}
class LenovoPaid extends Paid{
    @Override
    public void sale() {
        System.out.println("销售联想Paid");
    }
}

使用桥接模式将品牌和电脑类型区分开

品牌接口

public interface Brand {
    void sale();
}
class Lenovo implements Brand{
    @Override
    public void sale() {
        System.out.println("销售联想电脑");
    }
}
class Dell implements Brand{
    @Override
    public void sale() {
        System.out.println("销售戴尔电脑");
    }
}

电脑类

public  abstract class Computer2 {
    protected Brand brand;

    public Computer2(Brand brand) {
        this.brand = brand;
    }

    public void sale(){
        brand.sale();
    }
}

class Desktop2 extends Computer2{

    public Desktop2(Brand brand) {
        super(brand);
    }

    @Override
    public void sale() {
        super.sale();
        System.out.println("销售台式机");
    }
}

class Laptop2 extends Computer2{

    public Laptop2(Brand brand) {
        super(brand);
    }

    @Override
    public void sale() {
        super.sale();
        System.out.println("销售笔记本");
    }
}

桥接模式总结

  • 桥接模式可以取代多层继承的方案。多层继承违背了单一职责原则,复用性较差,类的个数也非常多。桥接模式可以极大的减少子类的个数,从而降低管理和维护的成本。
  • 桥接模式极大的提高了系统的系统可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有的系统,符合开闭原则。

桥接模式实际开发引用场景:

  • JDBC驱动程序
  • AWT的Peer架构
  • 银行日志管理
    • 格式分类:操作日志,交易日志,异常日志
    • 距离分类:本地记录日志,异地记录日志
  • 人力资源系统中的奖金计算模块
    • 奖金分类:个人奖金,团体奖金,激励奖金。
    • 部门分类:人士部门,销售部门,研发部门
  • OA系统的消息处理
    • 业务类型:普通消息,加急消息,特急消息。
    • 发送消息方式:系统内消息,手机短信,邮件。

组合模式

  • 使用组合模式的场景
    • 把部分和整体的关系用树型结构来表示,从而使客服端可以使用统一的方式处理部分对象和整体对象。
  • 组合模式核心
    • 抽象构件(Component)角色:定义了叶子和容器构件的共同点。
    • 叶子(Leaf)构件角色:无子节点。
    • 容器(Composite)构件角色:有容器特征,可以包含子节点。

组合模式工作流程分析︰

  • 组合模式为处理树形结构提供了完美的解决方案,描述了如何将容器和叶子进行递归组合,使得用户在使用时可以一致性的对待容器和叶子。
  • 当容器对象的指定方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员,并调用执行。其中,使用了递归调用的机制对整个结构进行处理。

代码示例

//抽象组件
public interface Component {
    void opreation();
}
interface Leaf extends Component{

}

interface Composite extends Component{
    void add(Component c);
    void remove(Component c);
    Component getchild(int index);
}


public interface AbstractFile {
    void killVirus();//杀毒方法
}

class ImagesFile implements AbstractFile{
    private String imagesname;

    public ImagesFile(String imagesname) {
        this.imagesname = imagesname;
    }

    @Override
    public void killVirus() {
        System.out.println("对图像文件"+imagesname+"进行查杀");
    }
}
class TextFile implements AbstractFile{
    private String textname;

    public TextFile(String imagesname) {
        this.textname = imagesname;
    }

    @Override
    public void killVirus() {
        System.out.println("对文本文件"+textname +"进行查杀");
    }
}

class Folder implements AbstractFile{
    private String Foldername;
    //定义容器来存放容器下的子节点
    private List<AbstractFile>list=new ArrayList<>();
    public Folder(String foldername) {
        Foldername = foldername;
    }

    void add(AbstractFile file){
        list.add(file);
    }



    @Override
    public void killVirus() {
        System.out.println("文件夹"+Foldername+"进行查杀");
        for (AbstractFile file:list){
            file.killVirus();
        }
    }
}
public class client {
    public static void main(String[] args) {
        AbstractFile f2,f3,f4;
        Folder f1=new Folder("我的收藏");
        f2=new ImagesFile("一张风景照.jpg");
        f3=new TextFile("日记文本");

//        f2.killVirus();
        f1.add(f2);
        f1.add(f3);
        f1.killVirus();
    }
}

开发中的应用场景

  • 操作系统的资源管理器。
  • GUI的容器层次图。
  • XML文件解析。
  • OA系统中,组织结构的处理。
  • Junit单元测试
    • 底层设计就是典型的组合模式,TestCase(叶子),TestUnite(容器),Test接口(抽象)。

装饰模式

职责:

  • 动态的为一个对象增加新的功能。
  • 装饰模式是一种用于代替继承的技术,无须通过继承增加子类就能扩展对象的新功能。使对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。

实现细节

  • component抽象构件角色
    • 真实对象和装饰对象有同样的接口,这样,客服端就能够以真实对象的方式同装饰对象交互。
    • ConcreteComponent具体构建角色(真实对象):
      • io流中FileinputStream,FileOutputStream
    • Decorator装饰角色:
      • 持有一个抽象构件的引用。装饰对象接受所有的客户端的要求,并把这些转发给真实的对象,这样就能在真实对象调用前后增加新的功能。
    • ConcreteDecorator具体装饰角色:
      • 负责给构件对象添加新的责任。

开发场景的使用:

  • IO中输入和输出流的设计
  • Swing包中图形界面构件功能
  • ServletAPI中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper类,增强了request对象的功能。
  • Strtus2中,request,response,session对象的处理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o64Cbd0p-1617868019405)(23种设计模式.assets/image-20210206125036738.png)]

外观模式

迪米特法则(最少知识原则)

  • 一个软件实体应当尽可能的少于其它实体类发生相互作用。

外观模式核心:

  • 为子系统提供统一的入口,封装子系统的复杂性,便于客户端调用。

享元模式

  • 场景:
    • 内存属于稀缺资源,不要随便浪费,如果有和多个完全相同或相似的对象,我们可以通过享元模式,节省内存。
  • 核心
    • 享元模式以共享的方式高效地支持大量细粒度对象的重用。
    • 享元对象能做到共享的关键是区分了内部状态和外部状态。
      • 内部状态:可以共享,不会随环境变化而改变。
      • 外部环境:不可以共享,会随环境改变而改变。
  • 享元模式实现
    • FlyweightFactory享元工厂类
      • 创建并管理享元对象,享元池一般设计成键值对。
    • FlyWeight抽象享元类
      • 通常是一个接口或抽象类,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态。
    • ConcreteFlyWeight具体享元类
      • 为内部状态提供成员变量进行存储。
    • UnsharedConcreteFlyWeight非共享享元类
      • 不能被共享的子类可以设计成未非共享享元类。、

代码示例

//享元类
public interface ChessFlyWeight {
    void setColor();
    String getColor();
    void display(Coordinate c);

}


class ConcreteChess implements ChessFlyWeight{

    private String color;

    public ConcreteChess(String color) {
        this.color = color;
    }

    @Override
    public void setColor() {

    }

    @Override
    public String getColor() {
        return color;
    }

    @Override
    public void display(Coordinate c) {
        System.out.println("棋子颜色:"+color+",位置x:"+c.getX()+",y:"+c.getY());
    }
}

//坐标类
//表示外部状态
public class Coordinate {
    private int x,y;

    public Coordinate(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}
//享元工厂类
public class ChessFlyWeightFactory {
    //享元池
    private static Map<String,ChessFlyWeight>map=new HashMap<>();

    public static ChessFlyWeight getChess(String color){
        if(map.get(color)!=null){
            return map.get(color);
        }else {
            ChessFlyWeight chessFlyWeight=new ConcreteChess(color);
            map.put(color,chessFlyWeight);
            return chessFlyWeight;
        }
    }
}
public class client {
    public static void main(String[] args) {
        ChessFlyWeight chessFlyWeight1=ChessFlyWeightFactory.getChess("黑色");
        ChessFlyWeight chessFlyWeight2=ChessFlyWeightFactory.getChess("黑色");
        System.out.println(chessFlyWeight1);
        System.out.println(chessFlyWeight2);
    }
}
//输出结果
//Algorithm.DesignPattern.flyweight.ConcreteChess@7cc355be
//Algorithm.DesignPattern.flyweight.ConcreteChess@7cc355be


享元模式的应用场景:

  • 享元模式由于其共享的特性,可以在任何池中操作,比如线程池,数据库连接池。
  • String类的设计也是享元模式。

优点:

  • 极大的减少了内存中对象的数量。
  • 相同的或相似的对象内存中只存一份,极大的节约资源,提高系统的性能。
  • 外部状态相对独立,不影响内部状态。

缺点:

  • 模式较为复杂,使程序逻辑复杂化。
  • 为了节约,共享了内部状态,分离出外部状态,而读取外部状态使运行时间变长。用时间换取了空间。

责任链模式

定义:

  • 将能够处理同一类请求的对象连成一条链,所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能这传递给链上的下一个对象。

场景:

  • 打牌时,轮流出牌。
  • 接力赛跑。
  • 大学中,奖学金审批。
  • 公司中,公文审批。

代码示例

//封装请假的基本信息
public class LeaveRequest {
    private String empName;
    private int  leaveDays;
    private String reason;

    public LeaveRequest(String empName, int leaveDays, String reason) {
        this.empName = empName;
        this.leaveDays = leaveDays;
        this.reason = reason;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public int getLeaveDays() {
        return leaveDays;
    }

    public void setLeaveDays(int leaveDays) {
        this.leaveDays = leaveDays;
    }

    public String getReason() {
        return reason;
    }

    public void setReason(String reason) {
        this.reason = reason;
    }
}
//领导的抽象类
public abstract class Leader {
    protected String name;
    protected Leader nextleader;
    public Leader(String name) {
        this.name = name;
    }
//    设置责任链上的后继对象
    public void setNextleader(Leader nextleader) {
        this.nextleader = nextleader;
    }
    public abstract void handleRequest(LeaveRequest request);
}

//主任类
public class Director extends Leader{
    public Director(String name) {
        super(name);
    }

    @Override
    public void handleRequest(LeaveRequest request) {
        if(request.getLeaveDays()<3){
            System.out.println(request.getEmpName()+"请假"+request.getLeaveDays()+",理由"+request.getReason());
            System.out.println("Director"+this.name+"审批通过");
        }else {
            if (this.nextleader!=null){
                this.nextleader.handleRequest(request);
            }
        }
    }
}

//经理类
public class Manager extends Leader{
    public Manager(String name) {
        super(name);
    }

    @Override
    public void handleRequest(LeaveRequest request) {
        if(request.getLeaveDays()<10){
            System.out.println(request.getEmpName()+"请假"+request.getLeaveDays()+",理由"+request.getReason());
            System.out.println("Manager"+this.name+"审批通过");
        }else {
            if (this.nextleader!=null){
                this.nextleader.handleRequest(request);
            }
        }
    }
}
//总裁类
public class GeneralManager extends Leader {
    public GeneralManager(String name) {
        super(name);
    }

    @Override
    public void handleRequest(LeaveRequest request) {
        if(request.getLeaveDays()<30){
            System.out.println(request.getEmpName()+"请假"+request.getLeaveDays()+",理由"+request.getReason());
            System.out.println("GeneralManager"+this.name+"审批通过");
        }else {
            System.out.println("请这么多天假,是不是要辞职啊");
        }
    }
}

测试类

public class client {
    public static void main(String[] args) {
        Leader a=new Director("张三");
        Leader b=new Manager("李四");
        Leader c=new GeneralManager("王五");

        //组织责任链对象的关系
        a.setNextleader(b);
        b.setNextleader(c);
        //开始请假操作
        LeaveRequest leaveRequest=new LeaveRequest("TOM",6,"回家谈情");
        a.handleRequest(leaveRequest);
    }
}
//测试结果
//TOM请假6,理由回家谈情
//Manager李四审批通过

开发中常见的场景:

  • java中,异常机制就是一种责任链模式。一个try可以对应多个catch,当第一个catch不匹配类型,则自动跳到第二个catch。
  • JavaScript语言中,事件冒泡和捕获机制。Java语言中,事件的处理采用观察者模式。
  • Servlet开发中,过滤器的链式处理。
  • Struts2中,拦截器的调用也是典序的责任链模式。

迭代器模式

场景

  • 提供一种可以遍历的聚合对象的方式。又称为:游标cursor模式。
  • 聚合对象:存储数据。
  • 迭代器:遍历数据。

代码示例

public interface Myiterator {
    void first(); //将游标指向第一个元素
    void next(); //将游标指向下一个对象
    boolean hasNext(); //判断是否存在下一个元素

    boolean isFirst();
    boolean isLast();

    Object getCurrentObj(); //获取当前游标的对象
}

public class ConcreteMyAggregate {
    private List<Object> list=new ArrayList<>();

    public void addObjext(Object o){
        this.list.add(o);
    }

    public void removeObj(Object o){
        this.list.remove(o);
    }

    public List<Object> getList() {
        return list;
    }

    public void setList(List<Object> list) {
        this.list = list;
    }

    //获取迭代器对象
    public Myiterator createIterator(){
        return new ConcreateIerator();
    }

    private class ConcreateIerator implements Myiterator{

        private int corsor;

        @Override
        public void first() {
            corsor=0;
        }

        @Override
        public void next() {
            if(corsor<list.size()){
                corsor++;
            }
        }

        @Override
        public boolean hasNext() {
            return corsor<list.size();
        }

        @Override
        public boolean isFirst() {
            return corsor==0;
        }

        @Override
        public boolean isLast() {
            return corsor==list.size()-1;
        }

        @Override
        public Object getCurrentObj() {
            return list.get(corsor);
        }
    }
}

public class client {
    public static void main(String[] args) {
        ConcreteMyAggregate concreteMyAggregate=new ConcreteMyAggregate();
        concreteMyAggregate.addObjext("aa");
        concreteMyAggregate.addObjext("bb");
        concreteMyAggregate.addObjext("cc");

        Myiterator myiterator=concreteMyAggregate.createIterator();
        while (myiterator.hasNext()){
            System.out.println(myiterator.getCurrentObj());
            myiterator.next();
        }

    }
}
//输出结果
//aa
//bb
//cc

中介者模式

核心:

  • 如果一个系统中对象的联系呈现为网状结构,对象之间存在大量多对多的关系,将导致关系及其复杂,这些对象称为“同事对象”
  • 我们可以引入一个中介者对象,使各个同事对象只跟中介者打交道。

代码示例

public interface Mediator {
    void register(String dname,Department department);
    void command(String dname);
}

public interface Department {
    void selfAction();//做自己部门的事情
    void outAction();//向总经理发出申请
}

public class Development implements Department {
    private Mediator mediator;

    public Development(Mediator mediator) {
        this.mediator = mediator;
        mediator.register("development",this);
    }

    @Override
    public void selfAction() {
        System.out.println("专心科研,开发项目!");
    }

    @Override
    public void outAction() {
        System.out.println("没钱了,需要资金支持!");
    }
}

public class finacial implements Department{
    private Mediator mediator;

    public finacial(Mediator mediator) {
        this.mediator = mediator;
        mediator.register("finacial",this);
    }

    @Override
    public void selfAction() {
        System.out.println("汇报工作,钱怎么花");
    }

    @Override
    public void outAction() {
        System.out.println("数钱!");
    }
}

public class Markte implements Department {
    private Mediator mediator;

    public Markte(Mediator mediator) {
        this.mediator = mediator;
        mediator.register("Market",this);
    }

    @Override
    public void selfAction() {
        System.out.println("汇报工作,承接项目,需要资金支持");
        mediator.command("finacial");
    }

    @Override
    public void outAction() {
        System.out.println("跑去接项目");
    }
}

public class President implements Mediator {

    private Map<String,Department>departmentMap=new HashMap<>();

    @Override
    public void register(String dname, Department department) {
        departmentMap.put(dname,department);
    }

    @Override
    public void command(String dname) {
        departmentMap.get(dname).selfAction();
    }
}

测试

public class client {
    public static void main(String[] args) {
        Mediator mediator=new President();
        Markte markte=new Markte(mediator);
        Development development=new Development(mediator);
        finacial finacial=new finacial(mediator);

        markte.selfAction();

    }
}
//测试结果
//汇报工作,承接项目,需要资金支持
//汇报工作,钱怎么花

中介者模式的本质:

解耦多个同事对象的交互关系,每个对象都持有中介者对象的引用,只跟中介者对象打交道。我们通过中介者对象统一管理这些交互关系。

开发中常见使用场景:

  • MVC模式(其中的C,控制器就是一个中介者对象。M和V都和他打交道)
  • 窗口游戏程序,窗口软件开发中窗口对象也是一个中介者对象。
  • 图形界面开发GUI中,多个组件之间的交互,可以通过引入一个中介者对象来解决。可以是整体的窗口对象或DOM对象。
  • Java.lang.reflect.MEthod#invoke()

命令模式

介绍:

  • 命令模式∶将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。也称之为:动作Action模式、事务transaction模式。

结构

  • Command抽象命令类
  • ConcreteCommand具体命令类
  • Invoker调用者/请求者
    • ·请求的发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联。在程序运行时,将调用命令对象的execute),间接调用接收者的相关操作。
  • Receiver接收者
    • 接收者执行与请求相关的操作,具体实现对请求的业务处理。
    • 未抽象前,实际执行操作内容的对象。
  • Client客户类
    • 在客户类中需要创建调用者对象、具体命令类对象,在创建具体命令对象时指定对应的接收者。发送者和接收者之间没有直接关系,都通过命令对象间接调用。

代码示例:

//真正的命令执行者
public class Receiver {
    public void action(){
        System.out.println("真正的执行命令");
    }
}
public interface Command {
    /*
      这是一个空的返回结果为方法。
      实际项目中,可以根据需求设计多个不同的方法
     */
    void execute();
}
class ConcreteCommand implements Command{

    private Receiver receiver;

    public ConcreteCommand(Receiver receiver) {
        this.receiver=receiver;
    }

    @Override
    public void execute() {
        //命令执行前后的操作及相关处理
        receiver.action();
    }
}
public class Invoke {
    //也可以通过容器容纳很多的命令对象,进行批处理
    private Command command;

    public Invoke(Command command) {
        this.command = command;
    }
    //业务方法,用于调用命令类的方法
    public void call(){
        command.execute();
    }
}
public class client {
    public static void main(String[] args) {
        Command c=new ConcreteCommand(new Receiver());
        Invoke invoke=new Invoke(c);
        invoke.call();
    }
}
//输出结果
//真正的执行命令

开发中常见的场景:

  • Struts2中,action的整个调用过程就有命令模式。
  • 数据库事务机制的底层实现。
  • 命令的撤销和恢复。

解释器模式

介绍:

  • –是一种不常用的设计模式

  • -用于描述如何构成一个简单的语言

    解释器,主要用于使用面向对象语言开戈的编译器和解释器设计。

  • -当我们需要开发一种新的语言时,可以考虑使用解释器模式。

  • -尽量不要使用解释器模式,后期维护会有很大麻烦。在项目中,可以使用Jruby , Groovy.(java的js引擎来替代解释器的作用,弥补java语言的不足。

开发中的使用场景

  • EL的表达式的处理
  • 正则表达式的解释器
  • SQL语言的解释器
  • 数学表达式的解析器

访问者模式

模式动机:

  • 对于存储在一个集合中的对象,他们可能是具有不同类型(即使有一个公共的接口)对于该集合中的对象,可以接受一类称为访问者的对象来访问,不同的访问者其访问方式也不一样。

定义:

  • 表示一个作用与某个对象结构中的各元素的操作,它可以使我们在不改变元素的类的前提下定义作用于这些元素的新操作。

开发中的场景:

  • XML文档的解析器
  • 编译器的设计
  • 复杂集合对象的处理

策略模式

场景

  • 某个市场人员接到单后的报价策略。报价策略很复杂,可以简单做如下分类。

    • 普通客户小批量报价。
    • 普通客户大批量报价。
    • 老客户小批量报价。
    • 老客户大批量报价。

    集体采用那个报价策略,这需要根据实际情况来确定,这时候,我们采用策略即可。

    如果使用普通的条件判断也可以实现,但是会造成代码过于庞大,不利于维护。

    代码示例

    public interface Strategy {
        double peice(double standrdPrice);
    }
    
    
    public class NewCoustomeFewStrategy implements Strategy {
        @Override
        public double peice(double standrdPrice) {
            System.out.println("新客户小批量,不打折,原价");
            return standrdPrice;
        }
    }
    
    class NewCoustomeManyStrategy implements Strategy{
    
        @Override
        public double peice(double standrdPrice) {
            System.out.println("新客户大批量,九折");
            return standrdPrice*0.9;
        }
    }
    class OldCoustomeFewStrategy implements Strategy{
    
        @Override
        public double peice(double standrdPrice) {
            System.out.println("老客户小批量,85折");
            return standrdPrice*0.85;
        }
    }
    class OldCoustomeManyStrategy implements Strategy{
    
        @Override
        public double peice(double standrdPrice) {
            System.out.println("老客户,大批量,打8折");
            return standrdPrice*0.8;
        }
    }
    
    public class Context {
        private Strategy strategy;
        //通过构造器注入或者set
        public Context(Strategy strategy) {
            this.strategy = strategy;
        }
    
        public void printPrice(double s){
            System.out.println("你该报价:"+strategy.peice(s));
        }
    
    }
    
    public class client {
        public static void main(String[] args) {
            Strategy strategy=new OldCoustomeFewStrategy();
            Context context=new Context(strategy);
    
            context.printPrice(998);
        }
    }
    

    本质:

    • 分离算法,选择实现。

    开发中常用场景:

    • JavaSe中GUI编程,布局管理。
    • Spring框架中,Resource接口,资源访问策略。
    • javax.sercelet.http.HttpServelet#service()

模板方法

模板方法介绍:

  • 模板方法模式是在编程中经常用到的模式,它定义了一个操作中算法骨架,将某些步骤延迟到子类中实现,这样,新的子类可以在不改变一个算法结构的前提下重新定义该算法的某些特定步骤。

核心

  • 处理某个流程的代码已经都具备,但是其中某个节点的代码暂时不能确定,因此。我们采用工厂模式,将这个节点的代码实现转移给子类完成。即:处理步骤父类中定义好,具体实现延迟子类中定义。

代码示例

public abstract class BankTempleteMethod {
    private void takeNumber(){
        System.out.println("排队取号");
    }

    public abstract void transact();

    private void evaluate(){
        System.out.println("反馈评分");
    }
    public final void process(){
        this.takeNumber();
        this.transact();
        this.evaluate();
    }
}
public class client {
    public static void main(String[] args) {
        BankTempleteMethod bankTempleteMethod=new DrawMoney();
        bankTempleteMethod.process();
    }
}

class DrawMoney extends BankTempleteMethod{

    @Override
    public void transact() {
        System.out.println("我要取款!!!");
    }
}

在软件开发中,我们可以将call翻译为调用,子类不能调用父类的方法,而通过父类调用子类。这些调用步骤已经在父类中写好了,完全有父类控制整个过程。

什么时候用到模板方法:

  • 实现一个算法时,整体步骤很固定。但是,某些部分易变。易变部分可以抽象处理,供子类实现。

开发中常见场景:

  • 非常频繁。各个框架,类库中都有它的影子,比如常见的有:
    • 数据库访问的封装。
    • Junit单元测试
    • servlet中关于doget和doPost方法调用
    • Hibernate中的程序模板。
    • spring中JDBCTemplate,HibernateTEmplate等。

状态模式

场景:

  • 电梯的运行
    • 维修,正常,关门,开门,向上运行,向下运行。
  • 红绿灯
    • 红灯,绿灯,黄灯。
  • 企业或政府系统
    • 公文的审批状态
      • 报销单据审批状态
      • 假条审批

核心:

  • 解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。

结构:

  • Context环境类
    • 环境类中维护一个State对象,他是定义了当前的状态。
  • State抽象状态类
  • ConcreteState具体状态类
    • 每一个类封装了一个状态对应的行为。

代码示例

public interface State {
    void handle();
}

public class FreeState implements State {
    @Override
    public void handle() {
        System.out.println("房间空闲!!没人入住");
    }
}

class BookedState implements State{

    @Override
    public void handle() {
        System.out.println("房间已经预定!");
    }
}

class CheckedState implements State{

    @Override
    public void handle() {
        System.out.println("客人已经入住!");
    }
}

//房间对象
public class HomeContext {
    private State state;

    public void setState(State state) {
        System.out.println("修改状态!");
        this.state = state;
        state.handle();
    }
}

//测试类
public class client {
    public static void main(String[] args) {
        HomeContext context=new HomeContext();
        context.setState(new FreeState());
        context.setState(new BookedState());
        context.setState(new CheckedState());
    }
}
//输出结果
//修改状态!
//房间空闲!!没人入住
//修改状态!
//房间已经预定!
//修改状态!
//客人已经入住!

开发中常见的场景:

  • 银行系统中账号系统的管理。
  • OA系统中公文状态的管理。
  • 酒店系统中,房间状态的管理。
  • 线程对象中各状态的切换。

观察者模式

场景:

  • 聊天室程序的创建腹务器创建好后,A,B,C丰个客户端连上来公开聊天。A向服务器友送数据,服务器端聊夭数据改变。我们希望将这些聊天数据分别发给其他在线的客户。也就是说,每个客户端需要更新服务器端得数据。
  • 一网站上,很多人订阅了” java主题吃新闻。当有这个主题新闻时,就会将这些新闻发给所有订阅的人。
  • 一大家一起玩CS游划,服务器需要将每个人的方位变化发给所有的客户。

上面这些场景,我们都可以使用观察者模式来处理。我们可以把多个订阅者、客户称之为观察者﹔需要同步给多个订阅者的数据封装到对象中,称之为目标。

核心:

  • 观察者模式主要用于1:N的通知。当一个对象(目标对象Subject或Objservable)的状态改变时,他需要即使告诉一系列对象(观察者对象,Observe人r),令他们做出反应。
  • 通知观察者的方式
      • 每次都会把通知以广播方式发送给所有观察者,所有观察者只能被动的接受。
      • 观察者是要知道有情况即可。至于什么时候获取内容,获取什么内容,都可以 自主决定。

代码示例

public class Subject {
    protected List<Observer>observerList=new ArrayList<>();

    public void register(Observer observer){
        observerList.add(observer);
    }

    public void remove(Observer observer){
        observerList.remove(observer);
    }

    //通知所有观察者跟新状态
    public void notifAllObservers(){
        for (Observer observer:observerList){
            observer.update(this);
        }
    }
}

public interface Observer {
    void update(Subject subject);
}

public class observerlmpl implements Observer{
    //myState需要和目标对象的state值保持一致
    private int myState;

    public int getMyState() {
        return myState;
    }

    public void setMyState(int myState) {
        this.myState = myState;
    }

    @Override
    public void update(Subject subject) {
        myState=((ConcreteSubject)subject).getState();
    }
}

public class ConcreteSubject extends Subject {
    private int state;

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
        //主题对象(目标对象)值发生了变化,请通知所有的观察者
        this.notifAllObservers();
    }
}

public class client {
    public static void main(String[] args) {
        //创建目标对象
        ConcreteSubject c=new ConcreteSubject();
        //创建多个观察者对象
        observerlmpl observer1=new observerlmpl();
        observerlmpl observer2=new observerlmpl();
        observerlmpl observer3=new observerlmpl();
        //将观察者添加到subject对象的观察这队伍中
        c.register(observer1);
        c.register(observer2);
        c.register(observer3);

        c.setState(3000);
        System.out.println("######################");
        System.out.println(observer1.getMyState());
        System.out.println(observer2.getMyState());
        System.out.println(observer3.getMyState());
    }
}

开发中常见的场景:

  • 聊天室程序,服务器转发给所有客户端。
  • 网络游戏。
  • 邮件订阅。
  • servle中,监听器的实现。

备忘录模式

场景:

  • 一录入大批人员资料。正在录入当前人资料时,发现上一个人录错了,此时需要恢复上一个人的资料,再进行修改。
  • Word文档编辑时,忽然电脑死机或断电,再打开时,可以看到word提示你恢复到以前的文档
  • 管理系统中,公文撤回功能。公文发送出去后,想撤回来。

核心:

  • 就是保存某个对象内部状态的拷贝,这样以后就可以将该对象恢复到原先的状态。

结构:

  • 源发器类
  • 备忘录类
  • 负责人类

代码示例

//备忘录类
public class Empmememto {
    private String ename;
    private int age;
    private int salary;

    public Empmememto(Emp emp) {
        this.age = emp.getAge();
        this.ename=emp.getEname();
        this.salary=emp.getSalary();
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }
}

//源发器类
public class Emp {
    private String ename;
    private int age;
    private int salary;

    //备忘录操作,并返回备忘录对象
    public Empmememto mememto(){
        return new Empmememto(this);
    }

    //进行数据恢复,恢复成制定备忘录的值
    public void recovery(Empmememto empmememto){
        this.age=empmememto.getAge();
        this.ename=empmememto.getEname();
        this.salary=empmememto.getSalary();
    }

    public Emp(String ename, int age, int salary) {
        this.ename = ename;
        this.age = age;
        this.salary = salary;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }
}

//负责人类
public class CareTaker {
    private Empmememto empmememto;

    public Empmememto getEmpmememto() {
        return empmememto;
    }

    public void setEmpmememto(Empmememto empmememto) {
        this.empmememto = empmememto;
    }
}

public class client {
    public static void main(String[] args) {
        CareTaker careTaker=new CareTaker();
        Emp emp1=new Emp("emp1",18,500);
        careTaker.setEmpmememto(emp1.mememto());
        System.out.println("第一次打印对象"+emp1.getEname()+","+emp1.getAge()+","+emp1.getSalary());
        emp1.setAge(20);
        emp1.setEname("emp2");
        emp1.setSalary(1000);
        System.out.println("第二次打印对象"+emp1.getEname()+","+emp1.getAge()+","+emp1.getSalary());
        emp1.recovery(careTaker.getEmpmememto());
        System.out.println("第三次打印对象"+emp1.getEname()+","+emp1.getAge()+","+emp1.getSalary());
    }
}
//输出结果
//第一次打印对象emp1,18,500
//第二次打印对象emp2,20,1000
//第三次打印对象emp1,18,500

备忘点较多时

  • 将备忘录压栈。
  • 将多个备忘录对象序列化和持久化。

开发中的场景:

  • 棋类游戏中的悔棋。
  • 普通软件的撤销操作。
  • 数据库中事物回滚。
  • Photoshop软件的历史记录。

标签:23,void,System,public,println,设计模式,class,out
来源: https://blog.csdn.net/humorious/article/details/115522473

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有