设计模式的作用、场景以及示例【快速入门,体验拉满,按需使用】

适配器模式

  • 作用

将一个类的接口变成客户端所期待的另一种接口,从而时不相匹配的两个类在一起工作。也称为变压器模式、包装模式(包装模式不止这一种)。让两个类可以工作在一起、提高类的复用率、增加透明性、灵活度高。

  • 场景

主要用于项目维护阶段

  • 示例

代码目录如下:

adapter/
├── sadapter  // 新增的适配器代码
│   ├── SecondUserAdapter.java
│   ├── SecondUserAddress.java
│   └── SecondUser.java
├── stable    // 已经在运行的代码,不可变
│   ├── FirstUser.java
│   └── IFirstUser.java
├── TestAdapter.java // 测试代码
└── updated   // 第三方提供的接口,不可变
    ├── ISecondUserAddress.java
    └── ISecondUser.java

正在运行部分:(stable)

public interface IFirstUser {
    void printInfo();
}
public class FirstUser implements IFirstUser {

    private String username;

    public FirstUser(String username) {
        this.username = username;
    }

    @Override
    public void printInfo() {
        System.out.println(this.username);
    }
}

需求添加部分(updated)

public interface ISecondUser {
    void printUsername();
}
public interface ISecondUserAddress {
    void printAddress();
}

为此新建适配器,分别新建两个类来实现接口

public class SecondUser implements ISecondUser {

    private String username;

    public SecondUser(String name) {
        this.username = name;
    }

    @Override
    public void printUsername() {
        System.out.print(username + " ");
    }
}

public class SecondUserAddress implements ISecondUserAddress {

    private String addr;

    public SecondUserAddress(String address) {
        this.addr = address;
    }

    @Override
    public void printAddress() {
        System.out.print(this.addr);
    }
}

适配器持有两个接口的引用,并实现原有接口

public class SecondUserAdapter implements IFirstUser {

    private ISecondUser iSecondUser;
    private ISecondUserAddress iSecondUserAddress;

    public SecondUserAdapter(ISecondUser iSecondUser, ISecondUserAddress iSecondUserAddress) {
        this.iSecondUser = iSecondUser;
        this.iSecondUserAddress = iSecondUserAddress;
    }

    @Override
    public void printInfo() {
        iSecondUser.printUsername();
        iSecondUserAddress.printAddress();
    }
}

测试代码

IFirstUser user1 = new FirstUser("User1");
user1.printInfo();
SecondUserAdapter userAdapter = 
    new SecondUserAdapter(new SecondUser("User2"),new SecondUserAddress("5 street"));
userAdapter.printInfo();

输出

User1
User2 5 street

监听者模式

  • 作用

降低对象之间的耦合度,进行解耦,便于模块化开发

当某些数据变化时,某些类会做出相应。处理数据的类主动投送消息,感兴趣的类主动订阅消息

  • 场景

Android开发有大量运用,Button控件的点击事件就是一个例子

  • 示例

以Android中Button的监听器为例,设计一个接口作为监听器,回调时利用handler控制调用的线程。

private Handler mMainHandler;
// 在主线程中运行
mMainHandler = new Handler(Looper.getMainLooper());

private void notifySthChange(final int state) {
    mMainHandler.post(new Runnable() {
        @Override
        public void run() {
            ArrayList<SListener> list = new ArrayList<>(mListeners);
            for(SListener l : list) {
                l.OnSthChanged(state);
            }
        }
    });
}

在回调中,可以直接更新UI。

桥接模式

  • 作用

将抽象和现实解耦,可以独立的变化

  • 场景

当一个类内部具备两种或多种变化维度时,使用桥接模式可以解耦这些变化的维度,使高层代码架构稳定

  • 示例

模拟工厂生产销售产品

文件目录

bridge/
|-- Client.java             // 测试代码
|-- factory
|   |-- CanFactory.java     // 罐头工厂
|   `-- ModernFactory.java  // 现代化工厂
|-- Factory.java            // 工厂的抽象类
|-- product
|   |-- Can.java            // 产品具体类 罐头
|   `-- Toy.java            // 产品具体类 玩具
`-- Product.java            // 产品的抽象类

产品类

public abstract class Product {

    protected void made() {
        System.out.println(getClass().getSimpleName() + " has been made");
    }
    protected void sold() {
        System.out.println(getClass().getSimpleName() + " has been sold");
    }

}

public class Can extends Product {

    @Override
    public void made() {
        super.made();
    }

    @Override
    public void sold() {
        super.sold();
    }
}

public class Toy extends Product {
    @Override
    public void made() {
        super.made();
    }

    @Override
    public void sold() {
        super.sold();
    }
}

工厂类

public abstract class Factory {

    private Product product; // 引用抽象的产品类

    public Factory(Product p) {
        this.product = p;
    }

    public void makeMoney() {
        product.made();
        product.sold();
    }
}

public class CanFactory extends Factory {

    // 只接受Can类
    public CanFactory(Can p) {
        super(p);
    }

    @Override
    public void makeMoney() {
        super.makeMoney();
        System.out.println(getClass().getSimpleName() + " make money!");
    }
}

public class ModernFactory extends Factory {
    // 只要是Product即可生产
    public ModernFactory(Product p) {
        super(p);
    }

    @Override
    public void makeMoney() {
        super.makeMoney();
        System.out.println("ModernFactory make money!");
    }
}

罐头工厂CanFactory只能生产罐头Can,而现代工厂ModernFactory可以生产Product,罐头或者玩具都可以。

测试与输出

Can can = new Can();
CanFactory canFactory = new CanFactory(can);
canFactory.makeMoney();
ModernFactory modernFactory = new ModernFactory(new Toy());
modernFactory.makeMoney();
/*
Can has been made
Can has been sold
CanFactory make money!
Toy has been made
Toy has been sold
ModernFactory make money!
*/

构造器模式

  • 作用

将复杂对象的构造与其表示分离,以便相同的构造过程可以创建不同的表示。也成为生成器模式。具有较好的封装性【客户端不必知道模型内部细节】、独立易扩展、方便控制风险

  • 场景
    • 类中有新增属性,需要扩充构造方法,单可选参数过多时,很容易传错参
    • 产品类复杂,客户端需要多项配置
  • 示例

AOSP中的AlertDialog.Builder

AlertDialog采用了Builder模式,通过builder可以对dialog进行配置。其中,dialog的各项属性可以设置默认值。

public class AlertDialog extends Dialog implements DialogInterface {
    public static class Builder {
        public Builder(Context context) {}
        public Builder setTitle(CharSequence title) {}
        public Builder setMessage(@StringRes int messageId) {}
        public Builder setPositiveButton(@StringRes int textId, final OnClickListener listener) {}
        public Builder setNegativeButton(@StringRes int textId, final OnClickListener listener) {}
        public Builder setCancelable(boolean cancelable) {}
        // ..................................................
        public AlertDialog create() {} //创建dialog
        public AlertDialog show() {} // 创建并显示真正的dialog
    }
}

JAVA场景下:

public class User implements Serializable {
    // ... Member variable\Construction method\Setter\Getter
    /**
     * User 类建造者类
     * @author test
     */
    private static class UserBuild {

        /**
         * 用户编号 required 成员变量
         */
        private Integer id ;

        /**
         * 用户名称 required 成员变量
         */
        private String name ;

        /**

         * 用户年龄 optional 成员变量
         */
        private Integer age ;

        /**
         * 用户身高 optional 成员变量
         */
        private Integer height ;

        public UserBuild(Integer id ,String name){
            this.id = id ;
            this.name = name ;
        }

        public UserBuild age(Integer age){
            this.age = age ;
            return this ;
        }

        public UserBuild height(Integer height){
            this.height = height ;
            return this ;
        }

        public User build(){
            return new User(this);
        }
    }

    public static void main(String[] args) {

        User user = new UserBuild(1,"2").age(3).height(4).build();
        System.out.println(user.toString());
        System.out.println(user.getId());
        System.out.println(user.getName());
        System.out.println(user.getAge());
        System.out.println(user.getHeight());
    }
}

命令模式

  • 作用

把请求封装成对象,可以在使用不同的请求把客户端参数化。对于请求排队或者记录请求入职提供命令的取消和恢复。这是高内聚。

  • 场景

比如餐厅单点,直接将需求封装成命令对象再交给厨师

  • 示例

文件格式

command/
|-- cmd
|   |-- Command.java // 命令抽象类 - 在这里是战术
|   |-- InsideCommand.java // 打内线
|   `-- ThreePointCommand.java // 三分战术
|-- Coach.java // 教练
|-- player     
|   |-- Center.java // 中锋
|   |-- Player.java // 运动员抽象类
|   |-- PointGuard.java    // 控卫
|   `-- SmallForward.java  // 小前锋
`-- TestMain.java // 测试代码

以篮球训练为例作为示例代码

// 抽象命令
public abstract class Command {
    // 可以调用的人员
    protected Center center = new Center();
    protected PointGuard pontiGurad = new PointForward();
    protected SmallForward smallForward = new SmallForward();
    
    public abstract void execute();
}

// 运动员抽象类
public abstract class Playter {
    public abstract void run();
    public abstract void shoot();
    public abstract void passBall();
    public abstract void catchBall();
    public abstract void dunk();
}

// 教练类
public class Coach {
    private Command command;
    
    public void setCommand(Command command){
        this.command = command;
    }
    
    public void action(){
        this.command.execute();
    }
}

// 各个人员类
public class Center extends Player {
    @Override
    public void run() {
        System.out.println("Center is running.");
    }

    @Override
    public void shoot() {
        System.out.println("Center shoots the ball");
    }
    // ... 其它命令
}

public class PointGuard extends Player {
    @Override
    public void run() {
        System.out.println("PointGuard is running.");
    }
    // ...与Center类似
}

public class SmallForward extends Player {
    @Override
    public void run() {
        System.out.println("SmallForward is running.");
    }
    // ...与Center类似
}

// 以下是简单两个命令
public class InsideCommand extends Command {
    public InsideCommand() {
        
    }
    @Override
    public void execute() {
        System.out.println(getClass().getSimpleName() + ":");
        super.pointGurad.catchBall();
        super.smallForward.run();
        // ...
    }
}
public class ThreePointCommand extends Command {
    // ...与InsideCommand类似
}

// 使用方式
Coach coach = new Coach();
Command command1 = new InsideCommand();
Command command2 = new ThreePointCommand();
coach.setCommand(command1);
coach.action();

装饰者模式

  • 作用

动态的给对象添加一些额外的职责。装饰模式比生成子类更加灵活,在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。

  • 装饰模式可以是继承关系的替代关系
  • 可以扩展一个类
  • 但是比较复杂

必须有一个接口或抽象类充当Componet抽象构建

  • 场景
    • 需要扩展一个类的功能
    • 需要为一批的兄弟类进行改装或者假装
  • 示例

文件结构

Component              // 最基本的抽象构件,接口或抽象类
    Operation()
ConcreteComponent      // 具体的构件
Decorator              // 抽象的装饰者
ConcreteComponent      // 具体的装饰者

AWorker作为装饰者,Plumber成为被装饰者。

puiblic class Decoration {
    public static void main(String args[]) {
        Plumber plumber = new Plumber();
        AWorker aWorker = new AWorker(plumber);
        aWorker.doSomeWork();
        // BWorker同理
    }
}

// Worker接口
interface Worker {
    void doSomeWork();
}

class Plumber implements Worker {
    public void doSomeWork () {
        System.out.println("Plumber do some work!");
    }
}

class Carpenter implements Worker {
    // 与Plumber同理
}

class AWorker implements Worker {
    private Worker tempWorker;
    
    public AWorker(Worker worker) {
        tempWorker = worker;
    }
    
    public void doSomeWork() {
        System.out.println("Hello, I am a AWorker");
        tempWorker.doSomeWork();
    }
}

class BWorker implements Worker {
    private Worker worker;
    
    public BWorker(Worker worker) {
        this.worker = worker;
    }
    
    public void doSomeWork() {
        System.out.println("Hello, I am a BWorker");\
        worker.doSomeWork();
    }
}

策略模式

  • 作用

可以定义一种算法,并把每个算法都封装起来,可以互相替换

  • 扩展性好,可以避免多重条件判断

  • 每个策略都是一个类,复用率小

  • 策略类需要对外暴露

  • 场景

    • 需要算法屏蔽规则的场景
    • 需要算法自由切换的场景
  • 示例

文件结构

strategy/
|-- Add.java            // 具体的策略
|-- Division.java       // 具体的策略
|-- IStrategy.java      // 策略接口
|-- Minus.java          // 具体的策略
|-- Paper.java          // 测试代码
`-- StrategyContext.java// 上下文角色,承上启下

具体代码

// 策略方法类
public interface IStrategy {
    float compute(float a, float b);
}

// 上下文类,用于屏蔽上层模块对策略的直接访问
public class StrategyContext {
    private IStrategy strategy;
    
    public void setStrategy(IStrategy strategy) {
        this.strategy = startegy;
    }
    
    public StrategyContext(IStrategy strategy) {
        this.strategy = strategy;
    }
    
    public float calculate(float a, float b) {
        return this.strategy.compute(a, b);
    }
}

// 具体的策略类
public class Add implements IStrategy {
    @Override
    public float compute(float a, float b) {
        return a + b;
    }
}

public class Minus implements IStrategy {
    // 与Add同理
}

public class Division implements IStrategy {
    // 与Add同理
}

// 使用方式
public static void main (String args[]) {
    StrategyContext context = new StrategyContext(new Add());
    System.out.println("Add Res = " + context.caculate(1,3));
    context.setStrategy(new Minus());
    System.out.println("Minus Res = " + context.caculate(1,3));
    // Division同理 
}

也可以用枚举类来封装策略

public enum Calculator {
    ADD("+") {
        public float exec(float a, float b) {
            return a + b;
        }
    },
    SUB("-") {
        public float exec(float a, float b) {
            return a - b;
        }
    }
    String value;
    
    Calculator(String _value) {
        this.value = _value;
    }
    
    public String getValue() {
        return value;
    }
    
    public abstract float exec(float a, float b);
}

// 使用方式
public static void main (String args[]) {
    System.out.println("ENUM ADD Res = " + Calculator.ADD.exec(12, 5));
    System.out.println("ENUM SUB Res = " + Calculator.SUB.exec(2,5));
}

工厂方法

  • 作用

可以首先定义一个用于创建对象的接口。

  • 方法创建一个新对象
  • 该方法的返回类型为抽象类/接口
  • 有若干个类实现了上述抽象模型

具体要实例化什么的类区别于客户代码调用哪一类的工厂方法

  • 场景

所有需要生成对象的地方都可以受用,但是要慎重考虑是否需要一个工厂类来管理。

  • 示例

iterator()方法就是工厂方法模式的很好例子。

// Iterator接口:
public interface Iterator<E> {
    boolean hasNext();
    E next();
    default void remove() {
        throw new UnsupportOperationException("remove");
    }
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while(hasNext())
            action.accept(next());
    }
}

// ArrayList中的iterator()方法返回一个ltr对象
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
    public Iterator<E> iterator() {
        return new Itr();
    }
    private class Itr implements Iterator<E> {
        // ...
    }
}

// LinkedList的iterator()返回的是另一种对象
// LinkerList<E> <-- AbstractSequentialList<E> <-- AbstractList<E>
public ListIterator<E> listIterator() {
    return listIterator(0);
}
public ListIterator<E> listIterator(final int index) {
    rangeCheckForAdd(index);
    
    return new ListItr(index);
}
private class ListItr extends Itr implements ListIterator<E> {
    // ......
}
private class Itr implements Iterator<E> {
    // ......
}

---- 后续待更新…

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-22 12:58:03       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-22 12:58:03       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-22 12:58:03       45 阅读
  4. Python语言-面向对象

    2024-07-22 12:58:03       55 阅读

热门阅读

  1. MCU常见相关术语缩写说明

    2024-07-22 12:58:03       14 阅读
  2. 【Statement对象】

    2024-07-22 12:58:03       18 阅读
  3. 基于深度学习的商品推荐

    2024-07-22 12:58:03       18 阅读
  4. 鸿蒙笔记--动画

    2024-07-22 12:58:03       18 阅读
  5. c++中的printf

    2024-07-22 12:58:03       15 阅读
  6. C语言14 强制类型转换

    2024-07-22 12:58:03       15 阅读
  7. Electron 的webContents.send和event.reply有什么区别

    2024-07-22 12:58:03       17 阅读
  8. LeeCode Practice Journal | Day20_Binary Tree07

    2024-07-22 12:58:03       19 阅读
  9. CSS(层叠样式表)选择器

    2024-07-22 12:58:03       18 阅读