设计模式实践示例

以下是我针对自己所掌握的知识出的设计模式题目,有不足的地方欢迎指摘。

一、设计模式原则

  1. 设计模式原则有哪些?
    1.开闭原则:对扩展开放,对修改关闭
    2.接口隔离原则:每个接口只完成单独业务的部分,不要将各种业务的功能糅合到同一个接口类中
    3.迪米特法则:如果两个实体无法直接通信,可以使用第三方连接两个实体,不要直接调用。目的是降低耦合度,提高模块的相对独立性
    4.依赖倒置原则:高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象
    5.里式替换原则:父类可以替换子类,但子类不可以替换父类
    6.单一职责原则:类尽量实现某一类功能,降低复杂度
    7.合成复用原则:能在类中定义尽量不要用继承。优先使用对象组合,而不是继承,类继承类似于“白箱复用”,对象组合类似于“黑箱复用”,类继承破坏了类的封装性。
  2. 每个原则对应有哪些设计模式?请举例
    1.开闭原则:抽象工厂模式、模板方法模式、装饰模式
    2.接口隔离原则:抽象工厂模式
    3.迪米特法则:适配器模式、中介者模式、观察者模式
    4.依赖倒置原则:抽象工厂模式、模板方法模式、工厂方法模式、命令模式
    5.里式替换原则:策略模式
    6.单一职责原则:职责链模式、中介者模式
    7.合成复用原则:组合模式
  3. 子类可以扩展父类的功能,但不能改变父类原有的功能。子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。该原则为:
    (开闭原则)
  4. 高层模块不应该依赖低层模块,两者都应该依赖其抽象。抽象不应该依赖细节,细节应该依赖抽象。面向接口编程,不要面向实现编程。该原则为:
    (依赖倒置)
  5. 要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。该原则为:
    (接口隔离)
  6. 现有一个系统,包含角色的增删改查、人员的增删改查,小明将角色和人员的增删改查放在了一个接口类中,由角色和人员的实现类实现方法,导致了接口方法的冗余。这种违背了什么原则?
    (接口隔离)
  7. 解释一下迪米特法则
    当两个实体无法直接通信时,不应该发生直接调用,而是应该使用第三方调用。目的是降低类的耦合度,提高模块的独立性
  8. 对扩展开放,对修改关闭,指的是什么原则?
    (开闭原则)
  9. 规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分。该原则是什么原则?
    (单一职责原则)

二、设计模式

  1. 23种设计模式按照目的划分,可以分为创建型模式、行为型模式、结构型模式。填写以下设计模式属于哪种目的类型
    工厂模式、建造者模式、中介者模式、适配器模式、组合模式、迭代器模式、访问者模式、观察者模式。
    创建型模式:工厂模式、建造者模式
    行为型模式:观察者模式、中介者模式、访问者模式、迭代器模式、访问者模式
    结构型模式:组合模式、建造者模式、适配器模式
  2. 看以下代码,属于哪种设计模式?
public interface PowerSupply5V {
    int dc5V();
}
public class PowerSupply220V {
    public int output220V() {
        return 220;
    }
}
public class Adapter implements PowerSupply5V {
    int ws = 0;

    public Adapter(PowerSupply220V powerSupply220V) {
        ws = powerSupply220V.output220V();
    }

    @Override
    public int dc5V() {
        return ws / 44;
    }
}
public class ClassAdapterTest {
    public static void main(String[] args) {
        PowerSupply5V powerSupply5V = new Adapter(new PowerSupply220V());
        int out = powerSupply5V.dc5V();
        log.info("out:{}", out);
    }
}

上面使用的是适配器模式
3. 工厂模式和抽象工厂模式都是创建对象的设计模式,两者什么区别?
工厂模式
抽象工厂模式
由图可以看出,工厂模式每种工厂都只生产一种产品,如果要扩展产品,需要再加工厂类,比较麻烦。抽象工厂模式,一种工厂可以生产多种产品,扩展产品只需要加方法即可。
4. 单例模式有懒汉模式、饿汉模式、枚举类型创建对象,三者什么区别?哪一种是线程安全的?请简单写一下这三种模式的代码。
单例模式即全局只有一个对象。
线程安全必须要保证在多线程的前提下只有一个对象被创建。
饿汉模式和枚举类型是线程安全的,懒汉模式中需要加synchronized和voliate才能实现线程安全。

1.饿汉模式

public class HungaryTest {
    private static final HungaryTest hungaryTest = new HungaryTest();
 
    private HungaryTest() {
    }
 
    public static HungaryTest getHungaryTest() {
        return hungaryTest;
    }
}

2.懒汉模式(不安全)

public class SafetyLazy2Test {
    private static SafetyLazy2Test safetyLazy2Test;
 
    private SafetyLazy2Test() {
    }
 
    public static SafetyLazy2Test getLazyTest() {
        if (safetyLazy2Test == null) {
            System.out.println("safetyLazy2Test是空");
            safetyLazy2Test = new SafetyLazy2Test();
        }
        System.out.println("safetyLazy2Test不是空");
        return safetyLazy2Test;
    }
}

3.懒汉模式(安全)

public class SafetyLazy2Test {
    private static volatile SafetyLazy2Test safetyLazy2Test;
 
    private SafetyLazy2Test() {
    }
 
    public static synchronized SafetyLazy2Test getLazyTest() {
        if (safetyLazy2Test == null) {
            System.out.println("safetyLazy2Test是空");
            safetyLazy2Test = new SafetyLazy2Test();
        }
        System.out.println("safetyLazy2Test不是空");
        return safetyLazy2Test;
    }
}

4.懒汉模式(双重检测)

public class SafetyLazyTest {
    private static volatile SafetyLazyTest safetyLazyTest;
 
    private SafetyLazyTest() {
    }
 
    public static SafetyLazyTest getLazyTest() {
        if (safetyLazyTest == null) {
            synchronized (SafetyLazyTest.class) {
                if (safetyLazyTest == null) {
                    System.out.println("lazyTest是空");
                    safetyLazyTest = new SafetyLazyTest();
                }
            }
        }
        System.out.println("lazyTest不是空");
        return safetyLazyTest;
    }
}

5.枚举(安全)

public enum  EnumSingleton {
    INSTANCE;
    public EnumSingleton getInstance(){
        return INSTANCE;
    }
}
  1. 把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。这种模式是什么模式?在项目中有没有用到过?如何使用的。
    这种模式是职责链模式
    在项目中主要用于处理某个请求,对不同类型的请求有不同的处理规则。
    在验证访客信息的时候,网关系统用到了职责链模式。访客有公司内部系统,外部访问人员等,针对公司内部系统,需要一个固定的凭证,就可以进入系统,但是对于外部访问人员,需要动态校验token值。
  2. 模板方法模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
    策略模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
    问:两者都和算法有关,两种模式区别是什么?有没有在项目中用到过两种模式?

    区别:
模板方式策略模式
所属行为模式
定义定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
目的完成某个功能,最终效果不一定相同。完成某个功能,最终的效果是相同的。
行为某个事件的流程相同,每个步骤可以有不同算法,相同步骤的不同算法不可以相互替换。同一个功能,但是具体算法可以不同,并且不同算法可以相互替换。

项目中使用这两种模式的场景:
1.模板方法模式:现有的jdk中,读取文件流抽象类InputStream中有read方法,其子类FileInputStream和BufferedInputStream分别实现了read方法,其中FileInputStream用于读取文件,BufferedInputStream用于读取数据流。两者都是用于处理输入流,但是是针对不同的类型的输入流进行处理
2.策略模式:排序算法比如冒泡、二分法等,中间处理过程不同,但是处理结果都是为了排序。

  1. 请写个建造者模式的例子。
    建造者模式的定义如下:是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。建造者模式按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)。
    建造者模式的类图如下:
    在这里插入图片描述
    建造者模式分为普通建造者模式以及注解式建造者模式,以注解式建造者模式为例:
    以“房”为例,无论是普通房子还是别墅,盖房(即建造者)步骤分为设计房屋结构、打地基、砌筑墙体、封顶、装修等六个步骤。房子(即产品)由设计图、地基、墙体、房顶、装修几部分组成。

1.房子:house

@Data
@Builder
public class House {
    /** 设计图 */
    private String design;
    /** 地基 */
    private String foundation;
    /** 墙体 */
    private String wall;
    /** 房顶 */
    private String roof;
    /** 装修 */
    private String decorate;
}

2.客户:client

public class Client {
    public static void main(String[] args) {
        House house = new House.HouseBuilder().design("图纸").foundation("地基").roof("房顶").decorate("装修").build();
        System.out.println("house:" + house.toString());
    }
}
  1. 原型模式浅拷贝和深拷贝的区别?
    浅拷贝只拷贝当前主类的对象,建立一个新的复制对象,但是新的复制对象中的子对象依旧引用之前的主类的子对象地址。
    深拷贝比浅拷贝更进一步,子对象也都重新开辟了新的内存存储,重写clone方法。
    平时使用的注解@Data实现了深拷贝
  2. 请描述一下状态模式。
    允许一个对象在其内部状态发生改变时改变其行为能力。对象的行为依赖于它的状态,并且可以根据它的状态改变而改变它的相关行为。每个状态通过持有Context的引用,来实现状态转移。
  3. 观察者模式和中介者模式的区别?
观察者模式中介者模式
所属行为模式
定义定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。也称为发布-订阅模式定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
适用场景

1.当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变;

2. 当一个对象必须通知其他对象,而它又不能假定其他对象是谁。

1.当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时;

2.当想创建一个运行于多个类之间的对象,又不想生成新的子类时。

目标群体 观察者模式中,目标类并不清楚观察者列表具体包含哪些对象; 中介者模式的通知模式是清楚要通知哪些对象的。
  1. 中介模式和命令模式的区别?
命令模式中介模式
所属行为模式
定义将一个请求封装为一个对象,使请求者和接收者分离开来,实现解耦。可以提供命令的撤销和恢复功能。定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
适用场景

1.系统需要支持命令的撤销操作和恢复操作。

2. 可以将命令组成队列执行

1.当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时;

2.当想创建一个运行于多个类之间的对象,又不想生成新的子类时。

相同点 都是为了让调用者和接收者解耦
  1. 迭代器的应用,代码举例
    提供一种方法来访问聚合对象,而不暴露这个对象的内部表示,其别名为游标。聚合对象提供数据以及数据访问功能,若聚合对象变化的话,数据访问也要同步修改,不符合开闭原则。在客户访问和聚合对象之间插入迭代器,实现解耦。迭代器模式可以为不同的容器提供一致的遍历行为,而不用关心容器内容元素组成结构
    使用ArrayList示例:
public class Client {
    public static void main(String[] args) {
        ArrayList<String> test = new ArrayList<>();
        test.add("1");
        test.add("2");
        test.add("3");
        test.add("4");
        test.add("5");

        //获取集合的迭代器
        Iterator<String> iterator = test.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}
  1. 请解释一下访问者模式。并且说一下其优缺点。
     访问者模式类图:
    访问者模式
    访问者模式是一种将数据结构与数据操作分离开来的行为模式。每个访问者标识一个作用于某对象结构中各元素的操作,它可以使你在不改变各元素的类的前提下定义作用于这些元素的新操作。
    优点:
    1.符合单一职责原则。各个访问者只需专注于自己的领域即可。
    2.扩展性好。可以扩展多个访问者以及元素。
    3.低耦合。结构和操作分离开,耦合度低。
    缺点:
    1.代码冗余,结构复杂。
    2.元素的结构一旦改变,则对应的具体元素类和访问类都要随之改变。

  2. 平时系统设计中有没有用到过备忘录模式?
    备忘录模式:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复至原来保存的状态。
    理解:当需要保存某个对象的操作数据或者该对象状态时,使用另外一个类记录原始对象的数据以及状态,以便回退到以前的某种数据。
    使用场景:
    1.需要保存或者恢复数据的相关状态场景。
    2.提供一个可回滚的操作。
    项目设计中,数据库表的备份或者容错机制用到了备忘录模式

  3. 请解释一下解释器模式。
    解释器模式:给定一个语言,定义它的文法的一种标识,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。解释器模式为自定义语言的设计和实现提供了一种解决方案,它用于定义一组文法规则并通过这组文法规则来解释语言中的句子。
    使用场景:
    1.可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
    2.一些重复出现的问题可以用一种简单的语言来进行表达。
    3.一个语言的文法较为简单。
    4.执行效率不是关键问题。
    项目设计中,比如计算机在计算加减乘除这些,就是用到了解释器模式
    解释器模式语法树

  4. 代理模式和中介者模式的区别
    从定义上来看,其实和中介者模式(行为模式)有相似之处,但是具体区分的话,其实在目的,角色划分上是不同的,
    1.目的:中介者模式是为了解耦,将访问者与被访问者解耦,避免直接关联;代理模式是为了控制对某对象的访问。
    2.角色:中介者模式分为四种角色:抽象中介类、具体中介类、抽象同事类、具体同事类;代理模式分为三种角色:抽象对象、目标对象、代理对象

  5. 项目中有没有用到过适配器模式?解释一下适配器。并说明一下和中介者的区别
    适配器模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
    项目运用中,当迁移系统时,如果不方便修改对外接口参数和返回时,可以添加适配器,将请求转化为新系统的请求。
    与中介者的区别:

适配器模式中介模式
所属行为模式结构模式
定义将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
适用场景 当迁移系统时,如果不方便修改对外接口参数和返回时,可以添加适配器,将请求转化为新系统的请求

1.当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时;

2.当想创建一个运行于多个类之间的对象,又不想生成新的子类时。

相同点 都有一个中间的对象对调用者和接收者进行转换。
  1. 说明一下桥接模式,并且写一下代码示例。
    桥接模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
    使用场景上:
    1.如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的集成联系,通过桥接模式可以使它们在抽象层建立一个关联关系
    2.对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
    3.一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
    桥接模式主要体现在组合关系和独立变化的特点上,可以使对象的实现有n种组合。以“顾客吃饭为例”,顾客的目的是方便面以及冰红茶,这两种产品有两种品牌:康师傅和统一。品牌即扩展属性。
    类图:
    桥接模式类图

1.Food类(食物)

public interface Food {
    /**
     * 吃饭
     */
    void eat();
}

2.InstantNoodles(方便面)

public class InstantNoodles implements Food {
    //品牌
    private Brand brand;

    //初始化品牌
    public InstantNoodles(Brand brand) {
        this.brand = brand;
    }

    @Override
    public void eat() {
        brand.brand();
        System.out.println("方便面");
    }
}

3.IceTea(冰红茶)

public class IceTea implements Food {
    private Brand brand;

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

    @Override
    public void eat() {
        brand.brand();
        System.out.println("冰红茶");
    }
}

4.Brand (品牌接口类)

public interface Brand {
    /**
     * 定义品牌
     */
    void brand();
}

5.KangShiFu(康师傅)

public class KangShiFu implements Brand{
    @Override
    public void brand() {
        System.out.println("康师傅牌子");
    }
}

6.TongYi(统一)

public class TongYi implements Brand{
    @Override
    public void brand() {
        System.out.println("统一牌子");
    }
}

7.Client(顾客)

public class Client {
    public static void main(String[] args) {
        //吃统一方便面
        InstantNoodles instantNoodles = new InstantNoodles(new TongYi());
        instantNoodles.eat();


        //喝康师傅冰红茶
        System.out.println();
        IceTea iceTea = new IceTea(new KangShiFu());
        iceTea.eat();
    }
}
  1. 装饰模式的理解
    装饰模式能够在不改变原来对象结构和功能的前提下,动态的给对象增加新的功能,相比于使用子类扩展的方式,装饰模式更加的灵活。
    类图:
    装饰模式
    使用场景:
    (1)需要扩展一个类的功能,或给一个类增加附加功能
    (2)需要动态地给一个对象增加功能,且这些功能可以再动态的撤销
    (3)需要为一批兄弟类进行改装或加装功能
    示例:
    以手机为例,从刚开始的基础版手机只具备打电话、发短信的功能,到后来可以安装QQ、浏览网页的功能,再到现在的智能手机可以安装各种各样的app,堪比一个小型电脑。手机的发展可以看做一个装饰者模式,以打电话、发短信为最基本的功能,QQ、浏览器、微信、王者荣耀等可以动态添加到手机的功能中。

  2. 外观模式和中介者模式的区别
    外观模式类图:
    外观模式类图
    中介者模式类图:
    中介者模式类图

中介模式外观模式
所属行为模式结构模式
定义定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
适用场景

1.当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。

2.当想创建一个运行于多个类之间的对象,又不想生成新的子类时。

1.为复杂的模块或子系统提供外界访问的模块

2.子系统相对独立。

3.一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。

相同点 都有一个中间的对象在调用者和接收者之间进行转换。
  1. 解释一下享元模式
    享元模式:它是通过共享技术来有效地支持大量细粒度对象的复用。
    类图:
    享元模式
    享元模式的特点有两个
    (1)内在状态:用于创建对象。
    (2)外在状态:外界传给实例的参数,用于实例执行某些方法。
    如何理解享元模式?
    刚开始看享元模式,这个模式感觉和单例模式很类似,两者区别是单例模式的关注点是创建单一对象,享元模式的关注点是规划创建对象。
    享元模式不太好理解,它的难点在于理解它的结构以及为什么会创造出这样的设计结构。我从一本书上的享元模式的例子中理解了享元模式的目的以及实现。书中的例子是以“文档编辑器”加载数据为例:
    通常上,文档编辑器上的文字或者字母,每个文字、字母都应该有一个存储
    在这里插入图片描述
    但是在物理结构上,却是相同的文字、字母共享同一个flyweight对象,从一个池中取数据,这种加载模式就是享元模式。
    在这里插入图片描述
    优点:
    (1)减少对象的创建,降低系统的内存,使效率提高。
    使用场景:
    (1)一个应用程序使用了大量的对象
    (2)完全由于使用大量的对象,造成很大的存储开销
    (3)对象的大多数状态都可变为外部状态
    (4)如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象
    (5)应用程序不依赖于对象标识,由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。

  2. 组合模式有没有用过?具体在什么情境下使用?
    组合模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
    类图:
    在这里插入图片描述
    一般在做树形菜单展示时使用组合模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值