行为型模式

1. 模板方法模式*

模板方法模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤

abstract class MySort {
    // 模板方法:
    // 提供了排序算法,但按什么顺序排没有实现
    public final void sort() {
        if (hook()) {
            orderBy();
        } else {
            System.out.println("默认排序规则");
        }
    }

    public abstract void orderBy();

    // 钩子方法:默认为不做任何事情,子类可以视情况去选择是否重写
    public boolean hook() {
        return true;
    }
}

class AscSort extends MySort {
    @Override
    public void orderBy() {
        System.out.println("Asc");
    }
}

class DescSort extends MySort {
    @Override
    public void orderBy() {
        System.out.println("Desc");
    }
}

class DefaultSort extends MySort {
    @Override
    public void orderBy() {
    }

    @Override
    public boolean hook() {
        return false;
    }
}

public class TemplateMethodClient {
    public static void main(String[] args) {
        MySort ascSort = new AscSort();
        MySort descSort = new DescSort();
        MySort defaultSort = new DefaultSort();
        ascSort.sort();
        descSort.sort();
        defaultSort.sort();
    }
}

2. 命令模式

命令模式:将一个请求封装为一个对象,让发出命令的责任和执行命令的责任分割开,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作

命令模式结构:

  1. Receiver:命令接收者,真正执行命令的对象
  2. Command:执行命令的接口,声明执行命令的方法
  3. ConcreteCommand:实现命令接口的命令对象,是“虚”的实现;通常会持有命令接收者,并调用命令接收者的功能来完成命令要执行的操作
  4. Invoker:调用命令对象去执行命令,通常会持有命令对象;这是使用命令对象的入口
  5. Client:创建具体的命令对象,并且设置命令对象的接收者

// 命令接收者:真正执行命令的对象
class Tv {
    public void turnOn() {
        System.out.println("电视机开机");
    }

    public void turnOff() {
        System.out.println("电视机关机");
    }
}

// 执行命令的接口
interface Command {
    void execute();

    void undo();
}

// 开机命令
class OnCommand implements Command {
    Tv tv;

    public OnCommand(Tv tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.turnOn();
    }

    @Override
    public void undo() {
        tv.turnOff();
    }
}

// 关机命令
class OffCommand implements Command {
    Tv tv;

    public OffCommand(Tv tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.turnOff();
    }

    @Override
    public void undo() {
        tv.turnOn();
    }
}

// 空命令:可以用来初始化Invoker中的每个命令
class EmptyCommand implements Command {
    @Override
    public void execute() {
    }

    @Override
    public void undo() {
    }
}

// 遥控器
class Invoker {
    Command onCommand = new EmptyCommand();
    Command offCommand = new EmptyCommand();

    public Invoker(Command onCommand, Command offCommand) {
        this.onCommand = onCommand;
        this.offCommand = offCommand;
    }

    public void turnOn() {
        onCommand.execute();
    }

    public void turnOff() {
        offCommand.execute();
    }
}

public class CommandClient {
    public static void main(String[] args) {
        Tv tv = new Tv();
        Invoker invoker = new Invoker(new OnCommand(tv), new OffCommand(tv));
        invoker.turnOn();
        invoker.turnOff();
    }
}

3. 访问者模式

访问者模式:把数据结构和作用于结构上的操作解耦合,使得可以在不改变各元素的类的前提下定义作用于这些元素的新操作

访问者模式适用于数据结构相对稳定、算法又易变化的系统,因为访问者模式使得算法操作增加变得容易

结构对象是使用访问者模式必备条件,而且这个结构对象必须存在遍历自身各个对象的方法。这便类似于Java语言当中的collection概念了

访问者模式涉及的角色:

  1. Visitor:抽象访问者角色,为该对象结构中的每一个具体元素角色声明一个访问操作接口
  2. ConcreteVisitor:具体访问者角色,实现Visitor声明的接口
  3. Element:定义一个接受访问的操作(accept()),它以一个访问者(Visitor)作为参数
  4. ConcreteElement:具体元素,实现了抽象元素(Element)所定义的接受操作接口
  5. ObjectStructure:结构对象角色,这是使用访问者模式必备的角色。它具备以下特性:能枚举它的元素;可以提供一个高层接口以允许访问者访问它的元素

据《大话设计模式》中说,访问者模式算是最复杂也是最难以理解的一种模式了!

import java.util.ArrayList;
import java.util.List;

// 抽象访问者角色
interface Visitor {
    void visitElementA(ConcreteElementA elementA);

    void visitElementB(ConcreteElementB elementB);
}

/**
 * 具体访问者角色:1
 * 不同的具体访问者,访问元素的方式可能不同
 */
class ConcreteVisitor1 implements Visitor {
    @Override
    public void visitElementA(ConcreteElementA elementA) {
        System.out.println("以方式1来访问元素A");
    }

    @Override
    public void visitElementB(ConcreteElementB elementB) {
        System.out.println("以方式1来访问元素B");
    }
}

// 具体访问者角色:2
class ConcreteVisitor2 implements Visitor {
    @Override
    public void visitElementA(ConcreteElementA elementA) {
        System.out.println("以方式2来访问元素A");
    }

    @Override
    public void visitElementB(ConcreteElementB elementB) {
        System.out.println("以方式2来访问元素B");
    }
}

// 定义一个接受访问的操作
interface Element {
    void accept(Visitor visitor);
}

/**
 * 具体元素:A
 * 使用了双分派:
 * 第一次分派:在客户端调用时将具体状态作为参数传递到ConcreteElementA中
 * 第二次分派:将ConcreteElementA对象本身作为参数传递到visitElementA中
 */
class ConcreteElementA implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visitElementA(this);
    }
}

// 具体元素:B
class ConcreteElementB implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visitElementB(this);
    }
}

// 结构对象角色
class ObjectStructure {
    List<Element> elementList = new ArrayList<>();

    public void attach(Element element) {
        elementList.add(element);
    }

    public void detach(Element element) {
        elementList.remove(element);
    }

    public void display(Visitor visitor) {
        for (Element element : elementList) {
            element.accept(visitor);
        }
    }
}

public class VisitorClient {
    public static void main(String[] args) {
        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.attach(new ConcreteElementA());
        objectStructure.attach(new ConcreteElementB());
        objectStructure.attach(new ConcreteElementA());
        objectStructure.display(new ConcreteVisitor1());
        System.out.println("----------------------");
        objectStructure.display(new ConcreteVisitor2());
    }
}

4. 迭代器模式

迭代器模式:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示

迭代器模式涉及的角色:

  1. Iterator(迭代器):定义访问和遍历元素的接口
  2. ConcreteIterator(具体迭代器):实现迭代器接口;对该聚合遍历时跟踪当前位置
  3. Aggregate(聚合):定义创建相应迭代器对象的接口
  4. ConcreteAggregate(具体聚合):实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

// 迭代器接口使用java现成的java.util.Iterator接口
// 具体迭代器
class ListIterator<E> implements Iterator<E> {
    List<E> list;
    // 要返回的下一个元素的索引
    int cursor = 0;
    // 返回的最后一个元素的索引
    int lastRet = -1;

    public ListIterator(List<E> list) {
        this.list = list;
    }

    @Override
    public boolean hasNext() {
        return cursor < list.size();
    }

    @Override
    public E next() {
        if (cursor >= list.size())
            throw new NoSuchElementException();
        lastRet = cursor;
        cursor++;
        return list.get(lastRet);
    }

    @Override
    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        list.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
    }
}

// 聚合
interface MyCollection {
    Iterator<String> getIterator();

    void addString(String s);
}

// 具体聚合
class MyList implements MyCollection {
    List<String> list = new ArrayList<>();

    @Override
    public ListIterator<String> getIterator() {
        return new ListIterator<>(list);
    }

    @Override
    public void addString(String s) {
        list.add(s);
    }
}

public class IteratorClient {
    public static void main(String[] args) {
        MyList myList = new MyList();
        myList.addString("1");
        myList.addString("2");
        myList.addString("3");
        ListIterator<String> listIterator = myList.getIterator();
        // 1 3
        while (listIterator.hasNext()) {
            String next = listIterator.next();
            if (next.equals("2"))
                listIterator.remove();
            else
                System.out.print(next + " ");
        }
    }
}

5. 观察者模式*

观察者模式:定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在观察者模式中,主体是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知

观察者模式涉及的角色:

  1. 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己
  2. 具体观察者(Concrete Observer):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调
  3. 抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象
  4. 具体主题(Concrete Subject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知

java.util.Observable类就使用了观察者模式

发布订阅机制就类似于观察者模式

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

// 需要更新的数据
@Data
@NoArgsConstructor
@AllArgsConstructor
class UpdatedData {
    String username;
    String password;
}

// 抽象观察者
interface Observer {
    void update(UpdatedData updatedData);
}

// 具体观察者:可以有多个
class ConcreteObserver implements Observer {
    UpdatedData updatedData = new UpdatedData();

    @Override
    public void update(UpdatedData updatedData) {
        this.updatedData = updatedData;
        System.out.println(this.toString() + "已更新:" + this.updatedData);
    }
}

// 抽象主题
interface Subject {
    void subscribe(Observer observer);

    void unsubscribe(Observer observer);

    void publish(UpdatedData updatedData);
}

// 具体主题:一般有1个
class ConcreteSubject implements Subject {
    List<Observer> observerList = new ArrayList<>();

    @Override
    public void subscribe(Observer observer) {
        System.out.println(observer + "订阅");
        observerList.add(observer);
    }

    @Override
    public void unsubscribe(Observer observer) {
        System.out.println(observer + "退订");
        if (observerList.contains(observer))
            observerList.remove(observer);
    }

    @Override
    public void publish(UpdatedData updatedData) {
        System.out.println("发布");
        for (Observer observer : observerList) {
            observer.update(updatedData);
        }
    }
}

public class ObserverClient {
    public static void main(String[] args) {
        Subject subject = new ConcreteSubject();
        Observer observer = new ConcreteObserver();
        subject.subscribe(observer);
        subject.publish(new UpdatedData("root", "123456"));
        subject.unsubscribe(observer);
    }
}

6. 中介者模式

中介者模式(调停者模式):用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互

中介者模式涉及的角色:

  1. 同事(Colleague):每一个同事都知道它的中介者对象,在需要与其他同事通信的时候,与它的中介者通信
  2. 具体同事(ConcreteColleague):实现同事接口
  3. 中介者(Mediator):中介者定义一个接口用于与各同事(Colleague)对象通信
  4. 具体中介者(ConcreteMediator):具体中介者通过协调各同事对象实现协作行为,了解并维护它的各个同事

import java.util.ArrayList;
import java.util.List;

// 同事
abstract class Colleague {
    Mediator mediator;

    public void setMediator(Mediator mediator) {
        this.mediator = mediator;
    }

    abstract void receive();

    abstract void send();
}

// 具体同事:1
class ConcreteColleague1 extends Colleague {
    @Override
    void receive() {
        System.out.println("具体同事1收到请求");
    }

    @Override
    void send() {
        System.out.println("具体同事1发出请求");
        // 请中介者转发
        mediator.relay(this);
    }
}

// 具体同事:2
class ConcreteColleague2 extends Colleague {
    @Override
    void receive() {
        System.out.println("具体同事2收到请求");
    }

    @Override
    void send() {
        System.out.println("具体同事2发出请求");
        // 请中介者转发
        mediator.relay(this);
    }
}

// 中介者
interface Mediator {
    void register(Colleague colleague);

    void relay(Colleague colleague);
}

// 具体中介者
class ConcreteMediator implements Mediator {
    List<Colleague> colleagueList = new ArrayList<>();

    @Override
    public void register(Colleague colleague) {
        if (!colleagueList.contains(colleague)) {
            colleagueList.add(colleague);
            colleague.setMediator(this);
        }
    }

    @Override
    public void relay(Colleague colleague) {
        // 给除了colleague的所有Colleague转发
        for (Colleague coll : colleagueList) {
            if (!coll.equals(colleague)) {
                coll.receive();
            }
        }
    }
}

public class MediatorClient {
    public static void main(String[] args) {
        Mediator mediator = new ConcreteMediator();
        Colleague colleague1 = new ConcreteColleague1();
        Colleague colleague2 = new ConcreteColleague2();
        mediator.register(colleague1);
        mediator.register(colleague2);
        colleague1.send();
        System.out.println("---------------------");
        colleague2.send();
    }
}

7. 备忘录模式

备忘录模式又叫做快照模式或Token模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态

备忘录模式涉及的角色:

  1. Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。Originator可以根据需要决定Memento存储自己的哪些内部状态。
  2. Memento(备忘录):负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。Originator却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。
  3. Caretaker(管理者):负责备忘录Memento,不能对Memento的内容进行访问或者操作。

import lombok.Data;

import java.util.ArrayList;
import java.util.List;

// 发起人
@Data
class Originator {
    String state;

    public void setStateFromMemento(Memento memento) {
        state = memento.getState();
    }

    public Memento createMementoToSaveState() {
        return new Memento(state);
    }
}

// 备忘录
class Memento {
    String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

// 管理者
class Caretaker {
    List<Memento> mementoList = new ArrayList<>();

    void add(Memento memento) {
        mementoList.add(memento);
    }

    Memento get(int index) {
        return mementoList.get(index);
    }
}

public class MementoClient {
    public static void main(String[] args) {
        Caretaker caretaker = new Caretaker();
        Originator originator = new Originator();
        originator.state = "状态0";
        caretaker.add(originator.createMementoToSaveState());
        originator.state = "状态1";
        caretaker.add(originator.createMementoToSaveState());
        // 当前状态:Originator(state=状态1)
        System.out.println("当前状态:" + originator);
        originator.setStateFromMemento(caretaker.get(0));
        // 恢复后,当前状态:Originator(state=状态0)
        System.out.println("恢复后,当前状态:" + originator);
    }
}

8. 解释器模式

解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

解释器模式涉及的角色:

  1. 上下文(Context):包含解释器之外的一些全局信息
  2. 抽象表达式(AbstractExpression):声明一个抽象的解释操作,这个接口为抽象语法树中所有的结点所共享。
  3. 终结符表达式(TerminalExpression):实现与文法中的终结符相关联的解释操作
  4. 非终结符表达式(NonterminalExpression):为文法中的非终结符实现解释操作;解释一般要递归地调用解释操作(Interpret)

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * 抽象表达式
 * Map<String, Integer> context充当上下文
 */
interface AbstractExpression {
    // Map中的key为变量名,value为变量值
    int interpret(Map<String, Integer> context);
}

// 终结符表达式
class TerminalExpression implements AbstractExpression {
    String key;

    public TerminalExpression(String key) {
        this.key = key;
    }

    // 根据变量名,然后对应的值
    @Override
    public int interpret(Map<String, Integer> context) {
        return context.get(this.key);
    }
}

/**
 * 非终结符表达式:
 * 每个运算符只和其左右两边的数字有关,
 * 但左右两边的数字也可能是一个解析结果,
 * 而且无论哪个解析结果,都是AbstractExpression类型
 */
abstract class NonterminalExpression implements AbstractExpression {
    AbstractExpression left;
    AbstractExpression right;

    public NonterminalExpression(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }
}

// 加运算符
class AddOperator extends NonterminalExpression {
    public AddOperator(AbstractExpression left, AbstractExpression right) {
        super(left, right);
    }

    @Override
    public int interpret(Map<String, Integer> context) {
        return super.left.interpret(context) + super.right.interpret(context);
    }
}

// 减运算符
class SubOperator extends NonterminalExpression {
    public SubOperator(AbstractExpression left, AbstractExpression right) {
        super(left, right);
    }

    @Override
    public int interpret(Map<String, Integer> context) {
        return super.left.interpret(context) - super.right.interpret(context);
    }
}

public class InterpreterClient {
    public static void main(String[] args) {
        System.out.println("定义变量:");
        Map<String, Integer> context = new HashMap<>();
        context.put("a", 10);
        context.put("b", 5);
        context.put("c", 6);
        context.entrySet().forEach(entry -> System.out.println(entry.getKey() + " = " + entry.getValue()));
        System.out.println("计算表达式:");
        String expr = "a + b - c";
        System.out.println(expr + " = " + calc(expr).interpret(context));
    }

    // 计算的规则
    public static AbstractExpression calc(String expr) {
        LinkedList<AbstractExpression> stack = new LinkedList<>();
        AbstractExpression left, right;
        for (int i = 0; i < expr.length(); i++) {
            switch (expr.charAt(i)) {
                case '+':
                    left = stack.pop();
                    right = new TerminalExpression(expr.charAt(++i) + "");
                    stack.push(new AddOperator(left, right));
                    break;
                case '-':
                    left = stack.pop();
                    right = new TerminalExpression(expr.charAt(++i) + "");
                    stack.push(new SubOperator(left, right));
                    break;
                case ' ':
                    break;
                default:
                    stack.push(new TerminalExpression(expr.charAt(i) + ""));
                    break;
            }
        }
        return stack.pop();
    }
}

9. 状态模式

状态模式:允许一个对象在其内部状态改变时改变它的行为

状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化

状态模式涉及的角色:

  1. 环境(Context):定义客户感兴趣的接口,维护一个ConcreteState子类的实例,这个实例定义当前状态
  2. 状态(State):定义一个接口以封装与Context的一个特定状态相关的行为
  3. 具体状态子类(ConcreteState):每一子类实现一个与Context的一个状态相关的行为

使用状态模式前,可以先画出状态转换图来分析各个状态间的关系

// 环境
class Context {
    State state;

    public Context(State state) {
        this.state = state;
    }

    void request() {
        state.handle(this);
    }
}

// 状态
interface State {
    void handle(Context context);
}

// 具体状态子类:A
class ConcreteStateA implements State {
    @Override
    public void handle(Context context) {
        context.state = new ConcreteStateB();
        System.out.println("已从状态A切换到状态B");
    }
}

// 具体状态子类:B
class ConcreteStateB implements State {
    @Override
    public void handle(Context context) {
        context.state = new ConcreteStateA();
        System.out.println("已从状态B切换到状态A");
    }
}

public class StateClient {
    public static void main(String[] args) {
        // 初始化状态为A
        Context context = new Context(new ConcreteStateA());
        context.request();
        context.request();
        context.request();
    }
}

10. 策略模式

策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换;策略模式使得算法可独立于使用它的客户而变化

策略模式涉及的角色:

  1. 策略(Strategy):定义所有支持的算法的公共接口;Context使用这个接口来调用某ConcreteStrategy
  2. 具体策略(ConcreteStrategy):实现Strategy接口的某个算法
  3. 上下文(Context):维护一个对Strategy对象的引用

java.util.Arrays就使用了策略模式:对自定义对象排序时,根据不同的排序策略来进行排序

// 策略
interface Strategy {
    void AlgorithmInterface();
}

// 具体策略A
class ConcreteStrategyA implements Strategy {
    @Override
    public void AlgorithmInterface() {
        System.out.println("正在使用策略A");
    }
}

// 具体策略B
class ConcreteStrategyB implements Strategy {
    @Override
    public void AlgorithmInterface() {
        System.out.println("正在使用策略B");
    }
}

// 上下文
class Context {
    Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void contextInterface() {
        this.strategy.AlgorithmInterface();
    }
}

public class StrategyClient {
    public static void main(String[] args) {
        // 默认使用策略A
        Context context = new Context(new ConcreteStrategyA());
        context.contextInterface();
        context.setStrategy(new ConcreteStrategyB());
        context.contextInterface();
    }
}

11. 责任链模式*

责任链模式:由每一个对象对其下家的引用而连接起来形成一条链,请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任

责任链模式涉及的角色:

  1. 抽象处理者角色(Handler):定义一个处理请求的接口,可以定义出一个方法以设定和返回对下家的引用
  2. 具体处理者角色(ConcreteHandler):接到请求后,如果可处理该请求,就处理之;否则将该请求转发给它的后继者

// 抽象处理者角色
abstract class Handler {
    protected int maxPriority;
    protected Handler successor;

    public Handler setSuccessor(Handler successor) {
        this.successor = successor;
        return this;
    }

    public final void request(int priority) {
        if (priority <= this.maxPriority) {
            handleRequest();
        } else if (successor != null) {
            successor.request(priority);
        } else {
            System.out.println("无法处理");
        }
    }

    abstract void handleRequest();
}

// 具体处理者角色:1
class ConcreteHandler1 extends Handler {
    public ConcreteHandler1(int maxPriority) {
        this.maxPriority = maxPriority;
    }

    @Override
    void handleRequest() {
        System.out.println("第一层处理");
    }
}

// 具体处理者角色:2
class ConcreteHandler2 extends Handler {
    public ConcreteHandler2(int maxPriority) {
        this.maxPriority = maxPriority;
    }

    @Override
    void handleRequest() {
        System.out.println("第二层处理");
    }
}

public class ChainOfResponsibilityClient {
    public static void main(String[] args) {
        // 所有请求,都从第一个Handler开始处理
        Handler handler = new ConcreteHandler1(10).setSuccessor(
                new ConcreteHandler2(100).setSuccessor(
                        null));
        handler.request(5);
        System.out.println("---------");
        handler.request(50);
        System.out.println("---------");
        handler.request(500);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值