详述java的设计模式(四)

 1.模板方法模式

模板方法模式是一种行为设计模式,它定义了一个操作中算法的骨架,将算法中不同的实现延迟到子类中。这个模式可以在不改变算法结构的前提下,使子类可以重新定义算法中的某些步骤,从而满足不同的需求。

模板方法模式是通过把不变行为搬移到超类,去除子类中的重复代码,使得子类可以集中于自己的行为的变化而不是系统整体的变化来设计的一种模式。

在模板方法模式中,超类通常定义一个模板方法,这个方法会在内部调用多个其他的抽象方法,子类需要实现这些抽象方法以定义自己的行为。这个模式可以非常好地应对开发中变化的需求,因为它允许在不改变算法结构的情况下,改变部分行为的实现方式。

假设我们要写一个文件读取器,它需要从文件中读取数据,并将数据转换成特定的格式。这个转换的过程可以由子类来实现,而读取文件的过程则是固定的,可以在父类中实现。以下是一个示例代码:

// 抽象文件读取器
public abstract class FileReader {
    // 模板方法
    public final void read() {
        open();
        readData();
        close();
    }
    
    // 打开文件
    protected void open() {
        System.out.println("打开文件");
    }
    
    // 读取文件数据,由子类实现
    protected abstract void readData();
    
    // 关闭文件
    protected void close() {
        System.out.println("关闭文件");
    }
}
// 文本文件读取器
public class TextFileReader extends FileReader {    
@Override    
protected void readData() {        
System.out.println("读取文本文件数据");    
}
}
// 图片文件读取器
public class ImageFileReader extends FileReader {
    @Override
    protected void readData() {
        System.out.println("读取图片文件数据");
    }
}

// 测试程序
public class Test {
    public static void main(String[] args) {
        // 使用文本文件读取器
        FileReader reader = new TextFileReader();
        reader.read();
        
        // 使用图片文件读取器
        reader = new ImageFileReader();
        reader.read();
    }
}

优缺点

模板方法模式(Template Method Pattern)是一种行为设计模式,它定义了一个操作中的算法骨架,将某些步骤延迟到子类中实现。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

在模板方法模式中,有一个抽象类定义了一个模板方法,它给出了一个算法的骨架,而将具体实现推迟到子类中去实现。抽象类通常还定义了一些基本的操作,这些操作可以由子类直接使用,也可以由子类进一步扩展和改进。

模板方法模式的优点是:

将算法的框架和具体实现分离,使得实现更加灵活;
可以避免重复代码,减少代码冗余;
使得子类可以在不改变算法结构的情况下重定义算法的某些特定步骤。
模板方法模式的缺点是:

由于算法的框架是固定的,因此如果算法框架需要改变,那么需要修改抽象类的代码,这可能会导致一些潜在的风险;
由于算法的实现被延迟到子类中,因此子类的数目可能会增加,增加了系统的复杂度。

模板模式 vs 策略模式

我在选择模板模式和策略模式时,发现两者都可以完全满足我的需求,然后我到网上查阅了很多资料,希望能找到两种模式在技术选择时,能确定告诉我哪些情况需要选择哪种模式,说来惭愧,到现在我都没有找到,因为网上只告诉我两种实现姿势的区别,但是没有说明如何具体选型,下面我就把我收集的资料,觉得比较核心的部分列出来,给大家一些参考。

有人请向我解释模板方法模式和策略模式之间有什么区别?

据我可以告诉他们是99%相同 – 唯一的区别是模板方法模式具有抽象类作为基类,而战略类使用由每个具体战略类实现的接口。

然而,就客户而言,他们的消费方式完全一样 – 这是正确的吗?

两者的主要区别在于具体algorithm的select。

使用Template方法模式时,通过子类化模板在编译时发生。 每个子类通过实现模板的抽象方法提供了一个不同的具体algorithm。 当客户端调用模板的外部接口的方法时,模板根据需要调用其抽象方法(其内部接口)来调用algorithm。

相比之下, 策略模式允许在运行时通过遏制来selectalgorithm。 具体algorithm是通过单独的类或函数实现的,这些类或函数作为parameter passing给构造函数或构造方法。 为此参数select哪种algorithm会根据程序的状态或inputdynamic变化。

综上所述:

  • 模板方法模式:通过子类化 编译时间algorithmselect

  • 策略模式:通过遏制 运行时algorithmselect

上面是完全摘抄网上的区别说明,只看到实现姿势的区别,但是如果通过这个就能指导我去选型,我觉得还不够,下面这个可能会讲的更具体一点:

相似:

  • 策略和模板方法模式都可以用来满足开闭原则,使得软件模块在不改变代码的情况下易于扩展。

  • 两种模式都表示通用function与该function的详细实现的分离。不过,它们所提供的粒度有一些差异。

差异:

  • 在策略中,客户和策略之间的耦合更加松散,而在模板方法中,两个模块耦合得更紧密。

  • 在策略中,虽然抽象类也可以根据具体情况而使用,但大多使用一个接口,而不使用具体类,而在Template方法中大多使用抽象类或具体类,不使用接口。

  • 在Strategy模式中,类的整体行为一般用接口表示,另一方面,Template方法用于减less代码重复,样板代码在基本框架或抽象类中定义。 在Template Method中,甚至可以有一个具有默认实现的具体类。

  • 简而言之,您可以在策略模式中更改整个策略(algorithm),但是在Template模式中,只有一些事情发生变化(algorithm的一部分),而其余事件保持不变。 在Template Method中,不变步骤是在一个抽象基类中实现的,而变体步骤要么是默认的实现,要么根本就没有实现。 在Template方法中,组件devise器强制执行algorithm所需的步骤和步骤的sorting,但允许组件客户端扩展或replace某些步骤。

看到上面的总结,感觉还是没有解答我的疑问,最后再引用一段网上的区别解读:

模板模式:

  • 它基于inheritance。

  • 定义不能被子类改变的algorithm的骨架。只有某些操作可以在子类中重写。

  • 父类完全控制algorithm ,仅将具体的步骤与具体的类进行区分。

  • 绑定是在编译时完成的。

策略模式:

  • 它基于授权/组成。

  • 它通过修改方法的行为来改变对象的内容。

  • 它用于在algorithm族之间切换。

  • 它在运行时通过在运行时用其他algorithm完全replace一个algorithm来改变对象的行为。

  • 绑定在运行时完成。

对于有强迫症的我,没有找到问题的根源,总感觉哪里不对劲,我就说一下我对于两者区别的理解吧。说实话,两种设计模式,我也就看到在实现姿势上有所区别,至于说的策略模式要定义统一接口,模板模式不这样做等等,我不太赞同,因为我有时也会给模板模式定义一个通用接口。然后也有人说,策略模式需要定义一堆对象,模板模式就不需要,如果有10个不同的企鹅,模板模式不也是需要定义10个不同的企鹅类,然后再专门针对特定的方法去实现么?

所以说,这两种设计模式,我感觉还没有到非此即彼的划分,我就是怎么爽就怎么用,比如企鹅模式的“吃饭、睡觉、打豆豆”,我不需要固定的执行流程,比如只去打豆豆,只需要对一个方法做具体抽象,我愿意选择策略模式,因为这个我感觉会让我需要使用的对象,更清晰一些。如果我有固定的执行流程,比如“吃饭、睡觉、打豆豆”,我更愿意使用模板方法,可能是代码看多了,也看习惯了,更愿意用模板方法去规范代码固定的执行流程。

当然,我也可以将两者结合起来使用,比如我们可以用模板方法,去实现这3只企鹅,但是对于middlePenguin,可能有分为企鹅少年A、企鹅少年B、企鹅少年C,他们都喜欢隔壁的企鹅妹妹,但是喜欢的方式不同,有暗恋的,有直接表白的,还有霸道总裁的,我可以用策略模式,去指定他们对企鹅妹妹的表达方式。

生活实例

模板方法模式的应用在生活中比较常见。比如说,我们想要做一道鱼香肉丝这道菜。一般来说,做鱼香肉丝需要以下步骤:备料、切菜、调料、炒菜、出锅。这些步骤的顺序不能改变,否则就无法做出一道正宗的鱼香肉丝。

那么我们可以把这些步骤抽象出来,定义一个模板类,包含这些步骤的执行顺序。每个具体的鱼香肉丝厨师可以继承这个模板类,并实现自己的具体做法。比如有的厨师喜欢加一点葱姜蒜,有的厨师喜欢放些辣椒,有的厨师会在炒之前把肉片先用淀粉抓匀。这些实现细节都是在子类中完成的,而模板方法模式确保了步骤的执行顺序不变。

这样做的好处是,当我们想要改变鱼香肉丝的做法时,只需要修改对应的子类即可。比如我们想要把鱼香肉丝做成甜的,就可以新建一个继承模板类的子类,重新实现调料的步骤即可。模板方法模式让代码的扩展性更好,也使得我们更容易修改代码而不会影响到其他的部分。

2.适配器模式

适配器模式是一种结构型设计模式,它的主要作用是让原本不兼容的接口能够相互合作。

适配器模式的代码示例:

目标接口、源接口

public interface Target {
    void request();
}
public class Adaptee {    
void specificRequest() {        
System.out.println("执行源接口的方法");   
}
}

适配器

public class Adapter implements Target {

    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    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();
    }
}

Adaptee假如是一个3.5mm的耳机插孔,Target是一个Type-C的耳机,Adapter就是一个转接头,将3.5mm的耳机插孔转换成Type-C的插孔。

描述

在上面的代码中,Target 接口是适配器希望实现的目标接口。Adaptee 类是已经存在的源接口,但是它的方法与 Target 接口不兼容。Adapter 类实现了 Target 接口,并在内部使用了 Adaptee 类来实现 request() 方法。通过使用 Adapter 类,客户端代码就可以调用 request() 方法,而不需要直接调用 specificRequest() 方法。

使用场景

1.已经存在的类的接口和需求不匹配。例如,你可以使用适配器模式来兼容旧的打印机,而无需更改客户端代码。

2.在一些类中使用第三方组件,这些组件的接口可能不符合系统设计标准,或者它们不提供必要的功能。适配器模式可以将这些组件的接口转换为系统所期望的接口,从而满足系统的需要。

3.需要使用已经存在的多个子类,但这些子类缺少某些共同的功能,这时适配器模式可以在不修改现有代码的情况下增加这些缺失的功能。

4.需要一个统一的输出格式来输出多个类的数据。这时,可以使用适配器模式将多个类的接口转换为统一的输出格式。

优缺点

类适配器
优点:可以根据需求重写Adaptee类的方法,使得Adapter的灵活性增强了。
缺点:有一定局限性。因为类适配器需要继承Target类,而Java是单继承机制,所以要求Adaptee类必须是接口。

对象适配器
优点:同一个Adapter可以把Adaptee类和他的子类都适配到目标接口。
缺点:需要重新定义Adaptee行为时,需要重新定义Adaptee的子类,并将适配器组合适配。

接口适配器
优点:可以灵活方便的选择性重写接口方法。
缺点:由于是匿名内部类的形式,所以不利于代码复用。

猜你喜欢

转载自blog.csdn.net/qq_18235445/article/details/129315666