设计模式课程总结一

什么是设计模式

定义:设计模式就是指在软件开发中,经过验证的,用于解决在特定环境下、重复出现的、特定问题的解决方案。

创建型模式:抽象了对象实例化进程,用来帮助创建对象的实例。

结构型模式:描述如何组合类和对象以获得更大的结构

行为型模式:描述算法和对象之间职责的分配

外观模式(Facade)

外观模式就是通过引入一个外观类,在这个类中定义客户端想要的简单的方法,然后在这些方法的实现里面,由外观类再去分别调用内部的多个模块来实现功能,从而让客户端变得简单,客户端只需要和外观类交互就可以啦
在这里插入图片描述

外观模式的目的不是给子系统添加新的功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而能让外部更简单的使用子系统

优点:

  • 外观类在系统这边,而不是在客户端,封装了系统内部实现的细节,松散耦合
  • 实现功能共享和复用
  • 开发简单,不需要去了解具体的子模块,简单易用
  • 更好的划分层次

在这里插入图片描述

本质:封装交互,简化调用

对设计原则的体现:最少知识原则

何时使用:

  • 复杂系统提供一个简单接口
  • 让客户程序和抽象类的实现部分松散耦合
  • 构建多层结构系统,使用外观对象作为每层的入口,简化层间调用

适配器模式(Adapter)

定义:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作

在这里插入图片描述

public interface Target{
    public void request();
}

需要被适配的对象:

public class Adaptee{
    public void specificRequest(){
        
    }
}

适配器对象:

public class Adapter implement Target{
    //需要被适配的对象
    private Adaptee adaptee;
    public Adapter(Adaptee adaptee){
        this.adaptee = adaptee;
    }
    public void request(){
        //调用已经实现的方法,进行适配
        adaptee.specificRequest();
    }
}

客户端:

public class Client{
    public static void main(String[] args){
        Adaptee adaptee = new Adaptee();
        Target target = new Adapter(adaptee);
        target.request();
    }
}

在这里插入图片描述
类适配器和对象适配器:

  • 实现上:类适配器使用对象继承的方式,是静态的定义方法;对象适配器使用对象组合的方式,是动态组合的方式。

适配器优缺点:

优点:

  • 更好的复用性
  • 更换的扩展性

缺点:

  • 过多的使用适配器,会让系统非常零乱,不容易整体进行把握

本质:转换匹配,复用功能

何时使用:

  • 想要使用已经存在的类,但是他的接口不符合需求
  • 你想创建一个可以复用的类,这个类可能和一些不兼容的类一起工作
  • 使用一些已经存在的子类,但是不可能对每一个子类都进行适配,这种情况使用适配器直接适配父类

单例模式(Singleton)

定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点

实现方式:要想控制一个类只被创建一个实例,首要问题是要把实例创建的权限收回,让类自身来负责自己类实例的创建工作,然后由这个类来提供外部可以访问这个类实例的方法。

在这里插入图片描述

Singleton:负责创建Singleton类自己的唯一实例,并提供一个getInstance的方法,让外部来访问这个类的唯一实例。

懒汉式:

public class Singleton{
    //定义变量存放创建好的实例,因为要在静态方法中使用,所以变量也必须是静态的
    private static Singleton uniqueInstance = null;
    //私有化构造方法,可以在内部控制创建实例的数目
    private Siingleton(){
        
    }
    //定义一个方法为客户端提供类实例,synchronized同步保证线程安全
    public static synchronized Singleton getInstance(){
        //判断是否已经有实例
        if(uniqueInstance == null){
            uniqueInstance = new Siingleton();
        }
        //有就直接用
        return uniqueInstance;
    }
}

饿汉式实现

public class Singleton{
    //定义一个变量来存储创建好的实例,直接在这里创建实例,只能创建一次,static变量在类加载时进行初始化,并且只被初始化一次。
    private static Singleton uniqueInstance = new Singleton();
       //私有化构造方法,可以在内部控制创建实例的数目,防止在外部创建
    private Siingleton(){
        
    }
    //定义一个方法为客户端提供类实例,方法上加static将该方法变为静态,目的是不需要对象实例就可以在外部直接通过类来调用
    public static Singleton getInstance(){
        //直接使用已经创建好的实例
        return uniqueInstance;
    }
}

饿汉式是在类装载时就创建对象实例,懒汉式是在使用对象实例时才会创建

单例的范围是虚拟机中的ClassLoader,如果有多个虚拟机或者一个虚拟机中有多个ClassLoader,单例就不起作用了。

优缺点:

懒汉式是时间换空间,饿汉是空间换时间

线程安全:

  • 不加同步的懒汉式是线程不安全的,加synchronized或者采用双重检查volatile
  • 饿汉式是线程安全的,因为虚拟机保证只会装载一次,在装载类的时候是不会并发的

何时使用:

当需要控制一个类的实例只能有一个,而且客户只能从一个全局访问点访问它时,可以选用单例模式。

观察者模式(Observer)

定义:定义对象间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

一个目标可以有任意多个观察者对象,一旦目标的状态发生了改变,所有注册的观察者都会得到通知,然后各个观察者会对通知做出相应的相应,执行相应的业务功能处理,并使自己的状态和目标对象的状态保持一致。

在这里插入图片描述

Subject:目标对象:

  • 一个目标可以被多个观察者观察
  • 目标提供对观察者注册和退订的维护
  • 当目标的状态发生变化时,目标负责通知所有注册的,有效的观察者

Observer:定义观察者的接口,提供目标通知时对应的更新方法。

ConcreteSubject:具体的目标实现对象,用来维护目标状态,当目标对象的状态发生改变时,通知所有注册的,有效的观察者,让观察者执行相应的处理。

ConcreteObserver:观察者的具体实现对象,用来接收目标的通知,并进行相应的后续处理,比如更新

自身的状态以保持和目标的相应状态一致。

代码:

目标对象:

//目标对象,它知道观察它的观察者,并提供注册和删除观察者的接口
public class Subject{
    //保存注册的观察者对象
    private List<Observer> observers = new ArrayList<Observer>();
    //注册观察者
    public void attach(Observer observer){
        observer.add(observer);
    }
    //删除
    publIc void detach()
    
    //通知
     public void notifyObservers()
}

具体的目标对象:

//具体的目标对象,负责把有关状态存入到相应的观察者对象,并在自己的状图发生改变时,通知各个观察者
public class ConcreteSubject extends Subject{
    private String subjectState;//目标对象状态
    public String getSubjectState(){
        return subjectState;
    }
    public void setSubjectState(String subjectState){
        this.subjectState =  subjectState;
        this.notifyObservers();//状态发生改变,通知每个观察者
    }
}

观察者接口定义:

//观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象
public interface Observer{
    public void update(Subject subject);//更新的接口
}

观察者的具体实现:

//具体观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致
public class ConcreteObserver implements Observer{
    private String observerState;
    public void update(Subject subject){
        //具体的更新实现
        observerState = ((ConcreteSubject)subject).getSubjectState();
    }
}

观察者分为两种模型:推模型和拉模型,他们的区别如下:

  • 推模型是假设目标对象知道观察者需要的数据;拉模型是目标对象不知道观察者需要什么数据,只能把自身传给观察者,让观察者自己按需取值
  • 推模型可能会使观察者对象难以复用,因为观察者定义的update方法是按需定义的,在出现新情况时,就需要提供新的update方法

优点:

  • 实现了观察者和目标之间的抽象耦合
  • 观察者模式实现了动态联动
  • 观察者模式支持广播通信

缺点:

  • 可能会引起无谓的操作

    因为观察者模式每次都是广播通信,不管观察者需不需要,每个观察者都会被调用update方法,如果观察者不需要执行相应处理,那么这次操作就浪费了。

何时使用:

  • 当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化,那么就可以选用观察者模式,将这两者封装成观察者和目标对象,当目标对象变化时,依赖于他的观察者对象也会发生变化。
  • 如果在更改一个对象的时候,需要同时联动改变其他的对象,并且不知道有多少个对象需要改变时,选用观察者模式。
  • 当一个对象必须要通知其他对象,但是又希望这个对象和被通知的对象之间是松散耦合的。

装饰模式(Decorator)

定义:动态的给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更加灵活。
在这里插入图片描述

Component:组件对象接口,可以给这些对象动态的添加职责

ConcreteComponent:具体的组件对象,实现组件对象接口,通常就是被装饰器装饰的原始对象,也就是可以给这个对象添加职责

Decorator:所有装饰器的抽象父类,需要定义一个与组件接口一致的接口,并持有一个被装饰的对象

ConcreteDecorator:实际的装饰器对象,实现具体要向被装饰对象添加的功能。

代码

组件对象的接口:

//组件对象的接口,可以给这些对象动态的添加职责
public abstract class Component{
    public abstract void operation();
}

实现组件的接口:

public class ConcreteComponent extends Component{
    public void operation(){
        
    }
}

抽象的装饰器对象:

//装饰器接口,维持一个指向组件对象的接口对象,并定义一个与组件接口一致的接口
public abstract calss Decorator extends Component{
    //持有组件对象
    protected Component component;
    
    public Decorator(Component component){
        this.component = component;
    }
    public void operation(){
        //转发请求给组件对象,可以在转发前后执行一些附加动作
        component.operation();
    }
}

装饰器实现对象:

public class ConcreteDecorationA extends Decorator{
    public ConcreteDecorationA(Component component){
        super(component);
    }
    private void addedBehavior(){
        //需要添加的职责实现
    }
    
    public void operation(){
        //调用父类的方法,可以在调用前后执行一些附加动作
       //在这里进行处理的时候,可以使用添加的状态
        super.operation();
        addedBehavior();
    }
}

装饰模式其实就是递归调用

在这里插入图片描述

功能:装饰器模式能够实现动态的为对象添加功能,是从一个对象外部来给对象增加功能,相当于改变了对象的外观。这样能够灵活的改变一个对象的功能,只要动态组合的装饰器发生了改变,那么最终所得到的对象的功能也就发生了改变。

装饰器功能能够复用,能够给一个对象多次增加同一个功能,也可以用同一个装饰器装饰不同的对象

原则:使用对象的组合而不是继承

本质:动态组合

优点:

  • 比继承更加灵活,继承是静态的,而装饰器是通过对象组合的方式动态的组合功能
  • 更容易复用
  • 简化高层定义,在高层定义的时候,不必把所有的功能都定义出来,因为装饰器能够为对象添加任意多的功能。

缺点:产生很多细粒度对象

何时使用:

  • 需要在不影响其他对象的情况下,以动态、透明的方式 给对象添加职责
  • 在不适合使用子类进行扩展的时候,考虑使用装饰模式

代理模式(Proxy)

定义:为其他对象提供一种代理以控制对这个对象的访问。

在这里插入图片描述

  • Proxy:代理对象

    实现与具体目标对象一样的接口,这样就可以使用代理对象来代替具体的目标对象了,保存一个指向具体的目标对象的引用,可以在需要的时候调用目标对象,可以控制对具体目标对象的访问,并可以负责创建和删除它

  • Subject:目标接口,定义代理和具体目标对象的接口,这样就可以在任何使用具体目标对象的地方使用代理对象。

  • RealSubject:具体的目标对象,真正实现目标接口要求的功能

对于代理对象,在创建的时候只需要装载用户编号和姓名就可以了,然后在客户需要访问除这两个属性外的数据时,再从数据库查询并装载数据,从而节省内存。客户端操作代理,代理操作真正的对象。

代理的分类:

  • 虚代理
  • 远程代理
  • copy-on-write代理
  • 保护代理
  • Cache代理
  • 防火墙代理
  • 同步代理
  • 智能指引

代理的特点:

代理模式在客户和被客户访问的对象之间,引入了一定程度的间接性,客户是直接使用代理,让代理来与被访问的对象进行交互。

本质:控制对象访问

何时使用:

  • 需要为一个对象在不同的地址空间提供局部代表的时候,可以使用远程代理
  • 需要按照需要创建开销很大的对象的时候,可以使用虚代理
  • 需要控制对原始对象的访问的时候,可以使用保护代理
  • 需要在访问对象执行一些附加操作的时候,可以使用智能指引代理

命令模式(Command)

定义:将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

在这里插入图片描述

Command:定义命令的接口,声明执行的方法,相当于机箱上的开机键

ConcreteCommand:命令接口实现对象,是虚的实现,通常会调用接收者的功能来完成命令要执行的操作,相当于开机键到主板的连接线,在这里面调用了主板的功能来实现开机

Receiver:接收者,真正执行命令的对象,任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能,相当于主板,执行开机的动作

Invoker:要求命令对象执行请求,通常会持有命令对象,可以持有很多命令对象,相当于机箱,持有开机键这个命令对象

Client:创建具体的命令对象,并且设置命令对象的接收者。

命令模式的关键:把请求封装成为对象,也就是命令对象,并定义了统一的执行操作的接口,这个命令对象可以被存储,转发,记录,处理,撤销等,整个命令模式都是围绕这个对象在进行

在这里插入图片描述

在这里插入图片描述

命令模式的优点:

  • 更松散耦合
  • 更动态的控制,命令模式将请求封装起来,可以动态的对它进行参数化,队列化和日志化等操作,从而使得系统更加灵活
  • 很自然的复合命令
  • 更好的扩展性

本质:封装请求

何时使用:

  • 如果需要抽象出需要执行的动作,并参数化这些对象,可以使用命令模式
  • 如果需要在不同的时刻指定、排列、和执行请求,可以选用命令模式
  • 如果需要支持取消操作,可以选用命令模式
  • 如果需要支持当系统崩溃时,能够将系统的操作功能重新执行一遍,可以选用命令模式
  • 在需要事务的系统中,可以选用命令模式

模板方法模式(Template Method)

定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
在这里插入图片描述

AbstractClass:抽象类。用来定义算法骨架和原语操作,具体的子类通过重定义这些原语操作来实现一个算法的各个步骤。

ConcreteClass:具体实现类。用来实现算法骨架中的某些步骤,完成与特定子类相关的功能。

代码

AbstractClass:

//定义模板方法,原语操作等抽象类
public abstract class AbstractClass{
    //原语操作1,抽象的方法,由子类实现
    public abstract void doPrimitiveOperation1();
    public abstract void doPrimitiveOperation2();
    
    //模板方法,定义算法骨架
    public final void templateMethod(){
        doPrimitiveOperation1();
        doPrimitiveOperation2();
    }
}

具体的实现类:

public class ConcreteClass extends AbstractClass{
    public void doPrimitiveOperation(){
        //具体的实现
    }
    public void doPrimitiveOperation2(){
        //具体的实现
    }
}

模板方法模式的功能在于固定算法骨架,而让具体算法实现可扩展

为什么模板方法是抽象方法而不是接口:

  • 接口是一种特殊的抽象类,所有接口中的属性自动是常量,所有接口中的方法是抽象的。
  • 抽象类不一定包含抽象方法,有抽象方法的类一定是抽象类。
  • 抽象类和接口比较,最大的特点就是抽象类中可以有具体的实现方法,而接口中所有的方法都是没有具体的实现的。
  • 既要约束子类的行为,又要为子类提供公共功能的时候使用抽象类。

把模板实现成为抽象类,为所有的子类提供公共的功能,就是定义了具体的算法骨架;同时在模板中把需要由子类扩展的具体步骤的算法定义为抽象方法,要求子类去实现这些方法,这就约束了子类的行为。

在java开发中,可以用java回调来实现模板方法模式

模板方法模式的优缺点

  • 实现代码复用
  • 算法骨架不容易升级

对设计原则的体现:开闭原则和里氏替换原则

何时使用模板方法模式:

  • 需要固定定义算法骨架,实现一个算法的不变部分,并把可变的行为留给子类来实现的情况
  • 各个子类中具有公共行为,应该抽取出来,集中在一个公共类中去实现,从而避免代码重复。
  • 需要控制子类扩展情况。

迭代器模式(Iterator)

定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需暴露该对象的内在表示。

在这里插入图片描述

Iterator:迭代器接口,定义访问和遍历元素的接口

ConcreteIterator:具体的迭代器实现对象。实现对聚合对象的遍历,并跟踪遍历时的当前位置

Aggregate:聚合对象。定义创建相应迭代器对象的接口

ConcreteAggregate:具体聚合对象。实现创建相应的迭代器对象。

迭代器接口:

Public interface Iterator{
    public void first();//移动到聚合对象的第一个位置
    public void next();//移动到下一个位置
    public boolean isDone();//判断是否是最后一个位置
    public Object currentItem();//获取当前位置元素
}

迭代器实现:

//具体的迭代器实现对象,下面是聚合对象为数组的迭代器,不同的聚合对象相应的迭代器实现是不一样的
public class ConcreteIterator implements Iterator{
    //持有被迭代的具体的聚合对象
    private ConcreteAggregate aggtegate;
    //内部索引,记录当前迭代到的索引位置
    private int index = -1;
    //构造方法,传入被迭代的具体的聚合对象
    public ConcreteIterator(ConcreteAggregate aggregate){
        this.aggregate = aggregate;
    }
    public void first(){
        iindex = 0;
    }
    public void next(){
        if(index<this,aggregate.size()){
            index=index+1;
        }
    }
    public boolean isDone{
        if(index == this.aggregate.size()){
            return true;
        }
        return false;
    }
    public Object currentItem(){
        return this.aggregate.get(index);
    }
}

聚合对象的定义:

//聚合对象接口,定义创建相应的迭代器对象的接口
public abstract class Aggregate{
    //工厂方法。创建相应的迭代器对象的接口
    public abstract Iterator createIterator();
}

聚合对象实现:

public class ConcreteAggregate extends Aggregate{
    private String[] ss = null;
    public ConcreteAggregate(String[] ss){
        this.ss = ss;
    }
    public Iterator createIterator(){
        //实现创建Iterator的工厂方法
        return new ConcreteIterator(this);
    }
    //获取索引所对应的元素
    public Object get(int index){
        Object retObj = null;
        if(index<ss.length){
            retObj = ss[index];
        }
        return retObj;
    }
    //获取聚合对象的大小
    public int size(){
        return this.ss.length;
    }
}

客户端代码:

public class Client{
    
}

功能:

  • 以不同的方式遍历聚合对象,比如向前,向后等
  • 对同一个聚合同时进行多个遍历
  • 以不同的遍历策略来遍历聚合,比如是否需要过滤
  • 多态迭代

优点:

  • 更好的封装性
  • 迭代器模式可以让你访问一个聚合对象的内容,而无需暴露给聚合对象的内部表示,从而提高聚合对象的封装性
  • 可以以不同的遍历方式来遍历一个聚合
  • 使用迭代器模式,使得聚合对象的内容和具体的迭代算法分离开。
  • 迭代器简化了聚合的 接口
  • 有了迭代器的接口,则聚合本身就不需要再定义这些接口了,从而简化了聚合的接口定义。
  • 简化客户端调用
  • 迭代器为遍历不同的聚合对象提供了一个统一的接口

状态模式(State)

定义:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

把每个状态所对应的功能处理封装在一个独立的类里面,这样选择不同处理的时候,其实就是在选择不同的状态处理类。并定义一个状态接口来约束这些状态类。
在这里插入图片描述

Context:环境,也称上下文,通常用来定义客户感兴趣的接口,同时维护一个具体的处理当前状态的实例对象

State:状态接口,用来封装与上下文的一个特定状态所对应的行为

ConcreteState:具体实现状态处理的类,每个类实现一个跟上下文相关的状态的具体处理。

状态模式的功能就是分离状态行为,通过维护状态的变化,来调用不同状态对应的不同功能。

状态模式的结构和策略模式的结构完全一样,但是他们的目的、实现、本质却完全不一样,还用行为之间的特性也是状态模式和策略模式一个重要区别,状态模式的行为是平行性的,不可相互替换;而策略模式的行为是平等性的,可以相互替换。

状态模式优点:

  • 简化应用逻辑控制
  • 更好的分离状态和行为
  • 更好的扩展性
  • 显式化进行状态转换

缺点:一个状态类对应一个状态处理类,会使得程序引入太多的状态类,使程序变得杂乱

何时使用:

  • 如果一个对象的行为取决于它的状态,而且它必须在运行时刻根据状态来改变它的行为,可以使用状态模式,来把状态和行为分开来。
  • 如果一个操作中有庞大的多分支语句,而且这些分支依赖于该对象的状态,可以使用状态模式。

策略模式(Strategy)

定义:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

在这里插入图片描述

Strategy:策略接口,用来约束一系列具体的策略算法。Context使用这个接口来调用具体的策略实现定义的算法。

ConcreteStrategy:具体的策略实现,也就是具体的算法实现

Context:上下文,负责和具体的策略类交互。

策略模式的功能是把具体的算法实现从具体的业务处理中独立出来,把它们实现成为单独的算法类,从而形成一系列的算法,并让这些算法可以相互替换。

策略模式的优点:

  • 定义一系列算法
  • 避免多重条件语句
  • 更好的扩展性

缺点:

  • 客户必须了解每种策略的不同
  • 增加了对象数目
  • 只适合扁平算法结构:策略模式的一系列算法是平等的

体现的设计原则:

  • 开闭原则
  • 里氏替换

何时使用:

  • 出现有许多相关的类,仅仅是行为有差别的情况下
  • 出现同一个算法,有很多不同实现的情况下
  • 需要封装算法中,有与算法相关数据的情况下
  • 替换if-else

组合模式(Composite)

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

组合模式通过引入一个抽象的组件对象,作为组合对象和叶子对象的父对象,这样就把组合对象和叶子对象统一起来了。
在这里插入图片描述

Component:抽象的组件对象,为组合中的对象声明接口,让客户端可以通过这个接口来访问和管理整个对象结构,可以在里面为定义的功能提供缺省的实现。

Leaf:叶子节点对象,定义和实现叶子对象的行为,不再包含其他的子节点对象

Composite:组合对象,通常会存储子组件,定义包含子组件的那些组件的行为,并实现在组件接口中定义的与子组件有关的操作。

Client:客户端,通过组件接口来操作组合结构里面的组件对象

优点:

  • 定义了包含基本对象和组合对象的类层次结构
  • 统一了组合对象和叶子对象
  • 简化了客户端调用
  • 更容易扩展

何时使用:

  • 想表示对象的部分-整体层次结构
  • 希望统一的使用组合结构中的所有对象

桥接模式(Bridge)

定义:将抽象部分和它的实现部分分离,使它们都可以独立的变化

桥接模式通过引入实现的接口,把实现部分从系统中分离出去,抽象那边用面向实现的接口编程,为了让抽象那边能够很方便的与实现结合起来,把顶层的抽象接口改成抽象类,在其中持有一个具体的实现部分的实例。

在这里插入图片描述

在这里插入图片描述
只要让抽象部分拥有实现部分的接口对象,就桥接上了,在抽象部分即可通过这个接口来调用具体实现部分的功能。抽象部分的实现通常需要调用实现部分的功能来实现。

优点:

  • 分离抽象和实现部分
  • 更好的扩展性
  • 可动态的切换实现
  • 可减少子类的个数

体现的设计原则:开闭原则,多用对象组合,少用继承

何时使用桥接:

  • 不希望在抽象部分和实现部分采用固定的绑定关系
  • 出现抽象部分和实现部分都能够扩展
  • 希望实现部分的修改不会对客户产生影响
  • 如果使用继承,会产生很多子类。

题目

1.写出三个oo设计原则,并说明在状态模式中是如何体现的

  • 单一职责原则:
    • 类的复杂性降低,实现什么职责都有清晰定义
    • 可读性提高,复杂性降低
    • 可维护性提高
    • 变更引起的风险降低
  • 里氏替换原则:子类可以替换父类并且不会出错
    • 子类完全实现父类方法
    • 子类可以有自己的个性
    • 覆盖或实现父类的方法时输入参数可以被放大
    • 覆盖或实现父类的方法时输出结果可以被缩小
  • 依赖倒置原则:1.高层模块不应该依赖底层模块,两者都应该依赖其抽象,2.抽象不应该依赖细节;3.细节应该依赖抽象
  • 接口隔离原则:只依赖需要的接口;建立单一接口,使接口最小化
  • 迪米特法则:一个对象应该对其他对象有最少的了解,1.只和朋友交流;2.朋友间也是有距离的
  • 开闭原则:对扩展开放,对修改关闭

状态模式中体现了单一职责原则,开闭原则,。单一职责体现在每一个状态都单独由一个状态实现类来实现,开闭原则体现在状态模式更好的扩展性,只需要新增加一个实现状态处理的公共接口的实现类即可。

2.写出宏命令的概念,并用代码实现宏命令

宏命令:就是包含多个命令的命令,是一个命令的组合

以取餐馆点菜为例:

1.定义接收者,也就是厨师

//厨师接口
public interface CookApi
    public void cook(String name);
//厨师对象,做热菜
public class HotCook implements CoolApi{
    public void cook(String name){
        System.out.println("在做"+name)
    }
}
//厨师对象,做凉菜
public class CoolCook implements CoolApi{
    public void cook(String name){
        System.out.println("在做"+name)
    }
}    

2.命令接口

public interface Command{
    public void execute();
}

3.命令对象,单个菜

public class ChopCommand implements Command{
    private CookApi cookApi = null;
    public void setCookApi(CookApi cookApi){
        this,cookApi = cookApi;
    }
    public void execute(){
        this.cookApi.cook("菜名")
    }
}

4.宏命令对象,菜单

public class MenuCommand implements Command{
    private Collection<Command> col = new ArryList<Command>();
    public void addCommand(Command cmd){
        col.add(cmd);
    }
    public void execute(){
        for(Command cmd:col){
            cmd.execute();
        }
    }
}

5.服务员

public class Waiter{
    //持有宏命令对象-菜单
    private MenuCommand menuCommand = new MenuCommand();
    //客户点菜,传进去的菜对象,也就是命令对象
    public void orderDish(Command cmd){
        CookApi hotCook = new HotCook;
        CookApi coolCook = new CoolCook();
        if(cmd instanceof DuckCommand){
            ((DuckCommand) cmd).setCookApi(hotCook);
        }else if{
        
    }
    menuCommand.addCommand(cmd);
       
    
    }
    public void orderOver(){
        this.menuCommand.execute();
    }

}

3.比较策略模式和状态模式

这两者从模式结构上看是一样的,但是实现的功能不一样。

状态模式是根据状态的变化来选择相应的行为,不同的状态对应不同的类,每个状态对应的类实现了该状态对应的功能,在实现功能的同时,还会维护状态数据的变化。这些实现状态对应的功能的类之间是不能相互替换的。

策略模式是根据需要或者是客户端的要求来选择相应的实现类,各个实现类是平等的,是可以相互替换的。另外策略模式可以让客户端来选择需要使用的策略算法;而状态模式一般是由上下文,或者是在状态实现类里面来维护具体的状态数据,通常不由客户端来制定状态。

4.比较透明组合和安全组合

安全性:从客户使用组合模式上看是否更安全,如果是安全的,就不会有误操作的可能,能访问的方法都是被支持的功能。

透明性是指:从客户使用组合模式上,是否需要区分到底是组合对象还是叶子对象。如果是透明的,那就不用再区分,对于客户而言,都是组件对象,具体的类型对于客户而言是透明的,是客户无需关心的。

如果把管理子组件的操作定义在Component中,那么客户端只需要面对Component.而无需关心具体的组件类型,这种实现方式就是透明性的

透明性是以安全性为代价的,因为在Component中定义的一些方法,对于叶子对象来说是没有意义的,比如增加删除子组件对象,客户不知道这些区别,就会误对叶子对象调用这些方法。

把管理子组件的操作定义在composite中,那么客户在使用叶子对象的时候,就没有增加删除子组件的功能,这种方式是安全的,但是客户端在使用的时候,就必须区分到底使用composite对象还是叶子对象,不通对象的功能是不一样的,这样对于用户来说就是不透明的了。

建议使用透明性实现方式。

5.说出建造者模式的适用场景

  • 如果创建对象的算法,应该独立于该对象的组成部分以及它们的装配方式时
  • 如果同一个构建过程有着不同的表示时。

6.什么是设计模式,如何使用设计模式目录

设计模式就是指在软件开发中,经过验证的,用于解决在特定环境下、重复出现的、特定问题的解决方案。

创建型模式:抽象了对象实例化进程,用来帮助创建对象的实例。

结构型模式:描述如何组合类和对象以获得更大的结构

行为型模式:描述算法和对象之间职责的分配

7.在桥接模式中,如何实现从实现中抽离解耦

桥接模式通过引入实现的接口,把实现部分从系统中分离出去,抽象那边用面向实现的接口编程,为了让抽象那边能够很方便的与实现结合起来,把顶层的抽象接口改成抽象类,在其中持有一个具体的实现部分的实例。

8.MVC如何体现设计模式

众所周知MVC不是设计模式,是一个比设计模式更大一点的模式,称作设计模式不合理,应该说MVC它是一种软件开发架构模式,它包含了很多的设计模式,最为密切是以下三种:Observer (观察者模式), Composite(组合模式)和Strategy(策略模式)。所以说MVC模式又称复合模式。

模型利用"观察者"让控制器和视图可以随最新的状态改变而更新。另一方面,视图和控制器则实现了"策略模式"。控制器是视图的行为; 视图内部使用"组合模"式来管理显示组件。

  • 模型使用观察者模式,以便观察者更新,同时保持两者之间的解耦。
  • 控制器是视图的策略,视图可以使用不同的控制器实现,得到不同的行为。
  • 视图使用组合模式实现用户界面,用户界面通常组合了嵌套的组件,像面板、框架和按钮。
  • 这些模式携手合作,把MVC模式的三层解耦,这样可以保持设计干净又有弹性。

9.享元模式如何实现对象复用且不是完全相同

把一个对象的状态 部分分成内部状态和外部状态,内部状态是不变的,外部状态是可变的,然后通过共享不变的部分,达到减少对象数量并节约内存的目的。在享元对象需要的时候,可以从外部传入外部状态给共享的对象,共享对象会在功能处理的时候,使用自己内部的状态和这些外部的状态,因为外部状态不同,所以实现了对象复用但不完全相同。

10.用代码实现一个单例模式

懒汉式:

public class Singleton{
    //定义变量存放创建好的实例,因为要在静态方法中使用,所以变量也必须是静态的
    private static Singleton uniqueInstance = null;
    //私有化构造方法,可以在内部控制创建实例的数目
    private Siingleton(){
        
    }
    //定义一个方法为客户端提供类实例,synchronized同步保证线程安全
    public static synchronized Singleton getInstance(){
        //判断是否已经有实例
        if(uniqueInstance == null){
            uniqueInstance = new Siingleton();
        }
        //有就直接用
        return uniqueInstance;
    }
}

饿汉式实现

public class Singleton{
    //定义一个变量来存储创建好的实例,直接在这里创建实例,只能创建一次,static变量在类加载时进行初始化,并且只被初始化一次。
    private static Singleton uniqueInstance = new Singleton();
       //私有化构造方法,可以在内部控制创建实例的数目,防止在外部创建
    private Siingleton(){
        
    }
    //定义一个方法为客户端提供类实例,方法上加static将该方法变为静态,目的是不需要对象实例就可以在外部直接通过类来调用
    public static Singleton getInstance(){
        //直接使用已经创建好的实例
        return uniqueInstance;
    }
}

11.写出一个双向适配器代码,并画出类图,以Cat会捉老鼠,Dog会bark为例,实现Cat Bark,Dog catch

在这里插入图片描述

public interface CatApi{
    public void Catch();
}
public class Cat implements CatApi{
    public Cat(){
        
    }
    public void Catch(){
        System.out.println("捉老鼠")
    }
}
public interface DogApi{
    public void Bark();
}
public class Dog implements DogApi{
    public Dog(){
        
    }
    public void Bark(){
        System.out.println("叫唤")
    }
}

public class Adapter implements CatApi,DogApi{
    private CatApi cat;
    private DogApi dog;
    public Adapter(CatApi cat,DogApi dog){
        this.cat = cat;
        this.dog = dog;
    }
    public void Catch(){
        dog.Bark();
    }
    public void Bark(){
        cat.Catch();
    }
    
}
public class Client{
    public static void main(String[] args){
        CatApi cat = new Cat();
        DogApi dog = new Dog();
        CatApi cat2 = new Adapter(cat,dog);
        DogApi dog2 = new Adapter(cat,dog);
        cat2.Catch();
        dog2.Bark();
        
    }
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值