软件构造心得(七)

设计模式

设计模式,即Design Patterns,是指在软件设计中,被反复使用的一种代码设计经验。 使用设计模式的目的是为了可重用代码,提高代码的可扩展性和可维护性

一:总纲
按照王老师的体系,23种设计模式,看上去很多,但万变不离其宗.一切设计模式,均是离不开这两张图.
具体来说,就如下两种:

1. 只有继承关系的:
DIP:依赖倒转原则,细节应当依赖于抽象,抽象不应当依赖于细节。
OCP:开闭原则,软件实体应当对扩展开放,对修改关闭.

特点:

只有一颗继承树

在设计模式里面的体现:

模板方法:比如,我写了一个白盒框架,现在要往里面加东西,我希望用户来进行手动添加,那么,我给用户一个接口,下面的子类型实现是可以进行任意安排的.

2. 继承+委托(delegation)

特点:两颗继承树

通过另一组继承树,对内在的实现进行了隐藏,具体的体现比如说:Factory方法,Adapter方法,装饰器模式,等等.

对于装饰器来说,可以认为这两颗树均是自己,通过自己对自己进行委托,进行功能的附加.

下面,我们来具体的对王老师上课所授的7种设计模式展开具体分析:

二:具体的7种设计模式
2.1 工厂方法:
工厂方法是一种创建型设计模式,它将对象的创建委托给子类。它定义了一个用于创建对象的接口,但让子类决定实例化哪个类。这样可以将一个类的实例化延迟到其子类中。工厂方法提供了一种灵活的方式来创建对象,同时也遵循了开放封闭原则,即对扩展开放,对修改封闭。

以下给出我利用工厂方法创建不同类型的日志记录器:

// 定义日志记录器接口
public interface Logger {
    void log(String message);
}

// 创建文件日志记录器
public class FileLogger implements Logger {
    public void log(String message) {
        System.out.println("Logging message to file: " + message);
    }
}

// 创建数据库日志记录器
public class DatabaseLogger implements Logger {
    public void log(String message) {
        System.out.println("Logging message to database: " + message);
    }
}

// 创建日志记录器工厂
public class LoggerFactory {
    public Logger getLogger(String loggerType) {
        if (loggerType.equalsIgnoreCase("file")) {
            return new FileLogger();
        } else if (loggerType.equalsIgnoreCase("database")) {
            return new DatabaseLogger();
        } else {
            throw new IllegalArgumentException("Invalid logger type");
        }
    }
}

// 使用日志记录器工厂创建并使用日志记录器
public class Main {
    public static void main(String[] args) {
        LoggerFactory factory = new LoggerFactory();
        Logger logger = factory.getLogger("file");
        logger.log("This is a log message");
    }
}

 

2.2 适配器模式

考虑下列情形:

假如我们在进行一个机器学习的项目,我们的核心算法,它需要提供一个numpy形式的矩阵,而我们的数据采集器,返回的矩阵,是一个excel文件.
这个时候,我们就需要一个适配器,帮我们干活了

它通过委托与接口,在二者之间建立了如下的桥梁;

2.3 装饰器模式
人靠衣装,在街上走,你会看到有穿T恤的,穿长袖夹克的,有穿背心的各种各样的人.
那么,如果要建立这样的关系的话,我们是可以通过继承来组合,也可以通过建立装饰器来进行委托.
如果是组合的话,问题会越来越麻烦.
设想,假如一开始所有人都穿T恤,那么到后来,秋天了,大家穿长袖的,有可能是里面套了一个背心,也有可能是里面传了一件毛衣,那么我们都用继承来进行处理的话,这个组合树,将会越来越大.问题很大!
这就是组合爆炸
所以,正确的做法是,让类学会穿衣服.不同的衣服,展现出不同的特性.有不同的功能
以下是我使用装饰器模式来动态地添加功能:

// 定义组件接口
public interface Component {
    void operation();
}

// 创建具体组件
public class ConcreteComponent implements Component {
    public void operation() {
        System.out.println("ConcreteComponent operation.");
    }
}

// 创建装饰器基类
public abstract class Decorator implements Component {
    protected Component component;
    
    public Decorator(Component component) {
        this.component = component;
    }
    
    public void operation() {
        component.operation();
    }
}

// 创建具体装饰器
public class ConcreteDecoratorA extends Decorator {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }
    
    public void operation() {
        super.operation();
        System.out.println("ConcreteDecoratorA operation.");
    }
}

// 创建另一个具体装饰器
public class ConcreteDecoratorB extends Decorator {
    public ConcreteDecoratorB(Component component) {
        super(component);
    }
    
    public void operation() {
        super.operation();
        System.out.println("ConcreteDecoratorB operation.");
        addedBehavior();
    }
    
    private void addedBehavior() {
        System.out.println("Added behavior in ConcreteDecoratorB.");
    }
}

// 使用装饰器动态地添加功能
public class Main {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        component.operation();
        
        System.out.println();
        
        Component decoratedComponentA = new ConcreteDecoratorA(component);
        decoratedComponentA.operation();
        
        System.out.println();
        
        Component decoratedComponentB = new ConcreteDecoratorB(decoratedComponentA);
        decoratedComponentB.operation();
    }
}

 

在上面的示例中,我们定义了一个Component接口来表示组件,和一个具体的实现ConcreteComponent。然后,我们创建了一个抽象的装饰器类Decorator,它维护了一个指向组件的引用,并实现了Component接口。接着,我们创建了两个具体的装饰器类ConcreteDecoratorAConcreteDecoratorB,它们都扩展了Decorator类,并在原有功能的基础上添加了新的行为。

Main类中,我们首先创建了一个ConcreteComponent对象,并调用了它的operation()方法。然后,我们创建了一个ConcreteDecoratorA对象,并将ConcreteComponent对象传递给它进行装饰。最后,我们创建了一个ConcreteDecoratorB对象,并将ConcreteDecoratorA对象传递给它进行装饰。这样,我们就可以动态地添加多个装饰器来扩展组件的功能。

2.4策略模式
考虑如下的场景:
假如我们在写一个文本翻译的APP,用户输入文本,我们对其进行翻译.
我们一开始是采用SVD分解对文本进行词嵌入.后来,竞争对手纷纷采用了BERT来进行词嵌入,效果比我们好很多.那么,我们也想要用BERT来进行,不过由于考虑到以后的扩展,我们决定将词嵌入,放到一个方法类里面去进行.
即这里的策略类.
策略模式:
存在多种算法来处理同一个任务,但client需要根据需要动态切换算法.
我们可以为不同的实现算法构造抽象接口.利用委托,在运行时,动态传入Client倾向的算法类的实例.

2.5 模板模式

模板模式是一种行为型设计模式,它定义了一个算法的骨架,将一些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。这种模式遵循了开放封闭原则,即对扩展开放,对修改封闭。

模板模式通常由一个抽象类和若干个具体子类组成,其中抽象类定义了算法的骨架,具体子类实现了算法中的具体步骤。在模板方法中,抽象类会定义一系列的抽象方法或受保护方法,这些方法将在具体子类中得到实现。

模板模式的优点是可以提高代码的复用性和可维护性,同时也可以使得算法的实现更加灵活和可扩展。缺点是增加了代码的抽象性,可能会使得代码更加难以理解和调试。

2.6 迭代器模式

用途:我们给外界用户的往往是一个黑箱,如果用户有想要逛逛的需求的话,我们便要在既维护内在表示的基础上,又满足外界用户的需求.
在Java里面已经提供了下面的接口:
Iterable接口:实现这个的集合对象是可遍历的
Iterator接口:迭代器,实现这个的是可以进行显示/隐式的进行迭代的.

以下是我使用迭代器模式来遍历一个集合:

// 定义迭代器接口
public interface Iterator {
    boolean hasNext();
    Object next();
}

// 创建具体迭代器
public class ConcreteIterator implements Iterator {
    private List<Object> list;
    private int index = 0;
    
    public ConcreteIterator(List<Object> list) {
        this.list = list;
    }
    
    public boolean hasNext() {
        return index < list.size();
    }
    
    public Object next() {
        if (hasNext()) {
            return list.get(index++);
        }
        return null;
    }
}

// 创建集合接口
public interface Aggregate {
    Iterator createIterator();
}

// 创建具体集合
public class ConcreteAggregate implements Aggregate {
    private List<Object> list = new ArrayList<>();
    
    public void add(Object obj) {
        list.add(obj);
    }
    
    public void remove(Object obj) {
        list.remove(obj);
    }
    
    public Iterator createIterator() {
        return new ConcreteIterator(list);
    }
}

// 使用迭代器遍历集合
public class Main {
    public static void main(String[] args) {
        Aggregate aggregate = new ConcreteAggregate();
        aggregate.add("A");
        aggregate.add("B");
        aggregate.add("C");
        
        Iterator iterator = aggregate.createIterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

在上面的示例中,我们定义了一个Iterator接口来表示迭代器,和一个具体的实现ConcreteIterator。然后,我们创建了一个Aggregate接口来表示集合,和一个具体的实现ConcreteAggregate。在ConcreteAggregate中,我们使用一个List来存储集合元素,并实现了createIterator()方法来返回一个ConcreteIterator对象。最后,在Main类中,我们创建了一个ConcreteAggregate对象,并向其中添加了三个元素。然后,我们创建了一个迭代器对象,并使用它来遍历集合中的元素。

迭代器模式的优点是可以提供一种统一的遍历集合元素的方式,同时也可以隐藏集合的内部实现细节。缺点是需要额外的迭代器类,可能会增加代码的复杂性。

2.7 Visitor模式

简单点说:就是预留扩展点,以便日后的扩展
为我们的对象的特定Visit,运行时进行动态的绑定.操作可以灵活更改的.方便以后操作.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值