设计模式面试系列-04

2022年7月17日
大约 11 分钟

设计模式面试系列-04

1. Java 中适配器模式有什么应用场景?

想要使用接口中的某个或某些方法,但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口,并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器。

举例:笔记本电脑的插头一般都是三相的,即除了阳极、阴极外,还有一个地极。而有些地方的电源插座却只有两极,没有地极。

电源插座与笔记本电脑的电源插头不匹配使得笔记本电脑无法使用。这时候一个三相到两相的转换器(适配器)就能解决此问题,而这正像是本模式所做的事情。

2. Java 中适配器模式有什么优缺点?

适配器模式优点

1)更好的复用性

系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。

2)更好的扩展性

在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。

适配器模式缺点

过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接

3. Java 中实现适配器模式有几种方式?

适配器模式属于结构型设计模式,它是用来将某个类/接口转换为客户端程序员期望的其他形式。如果有一个类与需要使用的接口不匹配时,可以使用适配器将这个类封装一层,使其可以使用接口中的方法。

适配器模式有两种方式,一种是使用继承(Inheritance),称为类适配器;另一种是使用委派(Delegation),称为对象适配器。有关详细的可以关注Java精选公众号。

4. Java 中如何实现类的适配器模式?

1、实现特殊功能类,代码如下:

package com.yoodb;
 
public class Adaptee {
    public void specificRequest() {
        System.out.println("具有特殊功能...");
    }
}

2、标准接口,代码如下:

package com.yoodb;
 
public interface Target {
    //标准接口
    public void request();
}

3、普通功能实现类,代码如下:

package com.yoodb;
 
public class ConcreteTarget implements Target {
    //具有普通功能
    @Override
    public void request() {
        // TODO Auto-generated method stub
        System.out.println("具有普通功能...");
    }
}

4、适配器类,代码如下:

package com.yoodb;
 
public class Adapter extends Adaptee implements Target{
    @Override
    public void request() {
        // TODO Auto-generated method stub
        super.specificRequest();
    }
     
}

5、测试函数,具体代码如下:

package com.yoodb;
 
public class ClientMain {
 
    //测试函数
    public static void main(String[] args) {
        //普通功能
        Target target = new ConcreteTarget();
        target.request(); 
        //特殊功能
        Target adapter = new Adapter();
        adapter.request();
    }
 
}

6、测试结果如下:

具有普通功能...
具有特殊功能...

5. Java 中如何实现对象的适配器模式?

1、实现特殊功能类,代码如下:

package com.yoodb;
 
public class Adaptee {
    public void specificRequest() {
        System.out.println("具有特殊功能...");
    }
}

2、标准接口,代码如下:

package com.yoodb;
 
public interface Target {
    //标准接口
    public void request();
}

3、普通功能实现类,代码如下:

package com.yoodb;
 
public class ConcreteTarget implements Target {
    //具有普通功能
    @Override
    public void request() {
        // TODO Auto-generated method stub
        System.out.println("具有普通功能...");
    }
}

4、适配器类直接实现标准接口,具体代码如下:

package com.yoodb;
 
public class Adapter implements Target{
     
    private Adaptee adaptee;
     
    @Override
    public void request() {
        // TODO Auto-generated method stub
        this.adaptee.specificRequest();
    }
 
    public Adapter(Adaptee adaptee) {
        super();
        this.adaptee = adaptee;
    }
     
}

5、测试函数,具体代码如下:

package com.yoodb;
 
public class ClientMain {
 
    //测试函数
    public static void main(String[] args) {
        //普通功能
        Target target = new ConcreteTarget();
        target.request(); 
        //特殊功能
        Target adapter = new Adapter();
        adapter.request();
    }
 
}

6、测试结果如下:

具有普通功能...
具有特殊功能...

6. Java 中什么是装饰模式?

装饰模式又名包装(Wrapper)模式,以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。

装饰模式动态的给对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活。

装饰模式的角色包含如下:

1)抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。

2)具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。

3)装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。

4)具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。

7. 装饰模式和适配器模式有什么区别?

装饰模式:对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。使用原来被装饰的类的一个子类的实例,把客户端的调用委派到被装饰类。

适配器模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。

从定义上看装饰模式是对核心对象或者功能的扩展,适配器模式是把对象或者功能放到一个新对象中引用。举个例子,现在书城卖道德经的书,有线装版,有精装版,有日文版,有英文版,其中线装版和精装版就是装饰模式,日文版和英文版就是适配器模式,各种版本都是为迎合不同消费者的不同需求。为什么呢?因为线装版和精装版的道德经虽然包装不同,但内容相同,日文版和英文版就不同,这两个版本的内容就可能和原版的不同,文化差异嘛,翻译的内容虽来自道德经,但根据不同国家的文化,思维逻辑什么的就可能改变一些想法。

使用条件

装饰模式一般在下列情况使用:需要扩展一个类的功能或者给你个类增加附加责任;需要动态的给一个对象增加功能,这些功能可以再动态的撤销;需要增加有一些基本功能的排列组合而产生非常大量的功能,从而使得继承关系变得不现实。

适配器模式一般使用的情况包括:系统需要使用现有的类,但此类已经不符合系统的需要;

想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的的类一起工作。适配器模式在系统升级的时候使用的频率很高,对旧系统的一些功能方法在新系统中引用。

8. 装饰模式和代理模式有什么区别?

1、装饰器模式强调的是增强自身,在被装饰之后你能够在被增强的类上使用增强后的功能。增强后你还是你,只不过能力更强了而已;代理模式强调要让别人帮你去做一些本身与你业务没有太多关系的职责(记录日志、设置缓存)。代理模式是为了实现对象的控制,因为被代理的对象往往难以直接获得或者是其内部不想暴露出来。

2、装饰模式是以对客户端透明的方式扩展对象的功能,是继承方案的一个替代方案;代理模式则是给一个对象提供一个代理对象,并由代理对象来控制对原有对象的引用;

3、装饰模式是为装饰的对象增强功能;而代理模式对代理的对象施加控制,但不对对象本身的功能进行增强;

9. Java 中如何实现装饰模式?

1、定义通用接口,代码如下:

package com.yoodb;
 
public interface Job {
    /**
     * 准备工作,以老鼠、猫、狗三种动物,做饭的小故事为例
     */
    public void prepare();
     
}

2、老鼠工作内容,代码如下:

package com.yoodb;
 
public class Mouse implements Job{
 
    @Override
    public void prepare() {
        // TODO Auto-generated method stub
        System.out.println("老鼠用锅盛满了水.\n");
    }
     
}

3、小猫工作内容,代码如下:

package com.yoodb;
 
public class Cat implements Job{
    //被装饰者
    private Job job;
 
    public Cat() {}
 
    public Cat(Job job) {
        this.job = job;
    }
 
    @Override
    public void prepare() {
        // TODO Auto-generated method stub
        //装饰者职责
        System.out.println("小猫正在准备柴进行点火工作.\n");
        //被装饰者职责
        job.prepare();
        //装饰者职责
        System.out.println("小猫将锅上架并把火点着了.\n");
    }
 
}

4、小狗工作内容,代码如下:

package com.yoodb;
 
public class Dag implements Job{
 
    private Job job;
     
    public Dag(Job job) {
        this.job = job;
    }
 
    public Dag() {}
 
    @Override
    public void prepare() {
        // TODO Auto-generated method stub
        //装饰者做的职责
        System.out.println("小狗正在做食材的准备工作\n");
        ///被装饰者职责
        job.prepare();
        //装饰者做的职责
        System.out.println("小狗已把食材准备成功,放入锅中!\n");
    }
}

5、测试Main,代码如下:

package com.yoodb;
 
public class DecoratorMain {
 
    public static void main(String[] args) {
        Job mouse = new Mouse();
        mouse.prepare();
        Job cat = new Cat(mouse);
        cat.prepare();
        Job dag = new Dag(cat);
        dag.prepare();
    }
     
}

6、输出结果如下:

老鼠用锅盛满了水.
小猫正在准备柴进行点火工作.
老鼠用锅盛满了水.
小猫将锅上架并把火点着了.
小狗正在做食材的准备工作
小猫正在准备柴进行点火工作.
老鼠用锅盛满了水.
小猫将锅上架并把火点着了.
小狗已把食材准备成功,放入锅中!

10. Java 中装饰模式有什么优缺点?

装饰模式优点

1、装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。装饰模式允许系统动态决定“贴上”一个需要的“装饰”,或者除掉一个不需要的“装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。

2、通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

装饰模式缺点

由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

11. Java 中装饰模式有什么应用场景?

1、需要扩展一个类的功能。

2、动态的为一个对象增加功能,而且还能动态撤销。

12. 抽象工厂模式和原型模式有什么区别?

抽象工厂模式:通常由工厂方法模式来实现。但一个工厂中往往含有多个工厂方法生成一系列的产品。这个模式强调的是客户代码一次保证只使用一个系列的产品。当要切换为另一个系列的产品,换一个工厂类即可。

原型模式:工厂方法的最大缺点就是,对应一个继承体系的产品类,要有一个同样复杂的工厂类的继承体系。我们可以把工厂类中的工厂方法放到产品类自身之中吗?如果这样的话,就可以将两个继承体系为一个。这也就是原型模式的思想,原型模式中的工厂方法为clone,它会返回一个拷贝(可以是浅拷贝,也可以是深拷贝,由设计者决定)。为了保证用户代码中到时可以通过指针调用clone来动态绑定地生成所需的具体的类。这些原型对象必须事先构造好。

原型模式想对工厂方法模式的另一个好处是,拷贝的效率一般对构造的效率要高。