责任链(Chain of Responsibility)模式

责任链模式是一种对象行为型模式,用于使多个对象有机会处理请求,减少发送者与接收者之间的耦合。在该模式中,请求沿着对象链传递,直到某个对象处理它。常见应用场景包括图形用户界面的帮助系统,允许对象根据通用性顺序处理请求。该模式降低了对象间的耦合,但也可能导致请求未被处理的情况。

责任链(Chain of Responsibility)模式

隶属类别——对象行为型模式


1. 意图

使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

2. 别名

3. 动机

考虑一个图形用户界面的上下文有关的帮助机制。用户在界面任一部分上点击就可以得到帮助信息。所提供的帮助依赖于点击的是界面的哪一部分以及其上下文。例如,对话框中的按钮的帮助信息就可能和主窗口中类似的按钮不同。如果对那一部分界面没有特定的帮助信息,那么帮助系统应该显示一个关于当前上下文的较一般的帮助信息——比如说,整个对话框。

因此很自然地,应根据普遍性(generality)即从最特殊到最普遍的顺序来组织帮助信息。而且,很明显,在这些用户界面对象中会有一个对象来处理帮助请求;至于是哪一个对象则取决于上下文以及可用的帮助具体到何种程度。

这儿的问题是提交帮助请求的对象(如按钮)并不明确知道谁是最终提供帮助的对象。我们要有一个办法将提交帮助的请求与可能提供帮助的对象解耦(decouple).Chain of Responsibility模式告诉我们应该怎么做。

这一模式的想法是,给多个对象处理一个请求的机会,从而解耦发送者和请求者。该请求沿对象链传递直至其中一个对象处理它,如下图所示:

在这里插入图片描述

从第一个对象开始,链中收到请求的对象要么亲自处理它,要么转发给链中的下一个候选者。提交请求的对象并不明确地知道哪一个对象将会处理它——我们说该请求有一个隐式的接受者(implicit receiver)。

假设用户在一个标有“Print”的按钮窗口组件上单击帮助,而该按钮包含一个PrintDialog的实例中,该实例知道它所属的应用对象(见前排的对象框图)。下面的交互框图(diagram)说明了帮助请求怎样沿链传递:
在这里插入图片描述

在这个例子,既不是aPrintButton也不是aPrintDialog处理该请求;它一直被传递给anApplication,anApplication 处理它或忽略它。提交请求的客户不直接引用最终响应它的对象。

要沿链转发请求,并保证接受者为隐式的(implicit),每个在链上的对象都有一致的处理请求和访问链上后继者的接口。例如,帮助系统可定义一个带有相应的HelpHandler操作的HelpHandler类。HelpHandler可为所有候选对象类的父类,或者它可被定义为一个混入(maxin)类。这样想处理帮助请求的类就可将HelpHandler作为其父类,如下图所示:
在这里插入图片描述

4. 适用性

在以下条件下使用Responsibility链:

  • 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
  • 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  • 可处理一个请求的对象集合应该被动态指定。

5. 结构

在这里插入图片描述
一个经典的对象结构可能如下图所示:

在这里插入图片描述

6. 参与者

  • Handler(如HelpHandler)
    • 定义一个处理请求的接口
    • 当作为所有ConcreteHandler的接口时,需要设置后续链的接口,当作为责任链最后的结尾可以不用设置后续接口
  • ConcreteHandler(如PrintButton和PrintDialog)
    • 处理它所负责的请求。
    • 可访问它的后继者
    • 如果可处理该请求,就处理这个请求,否则将该请求转发给它的后继者。
  • Client
    • 向链上的具体处理者(ConcreteHandler)对象提交请求。

7. 协作

  • 当客户提交了一个请求时,请求沿链传递直至有一个ConcreteHandler对象负责处理它。

8. 效果

Responsibility链有下列优点

    1. 降低耦合度 该模式使得一个对象无需知道是其他哪一个对象的其请求。对象仅需知道该请求会被“正确”地处理。接受者和发送者都没有对方的明确的信息,且链中的对象不需知道链的结构。

    结果是,责任链可以简化对象的相互连接。它们仅需保持一个指向其后继者的引用,而不需要保持它所有的候选接受者的引用。

    1. 增强了给对象指派责任(Responsibility)的灵活性 当在对象中分配职责时,职责链给你更多的灵活性,你可以通过在运行时刻对该链进行动态的增加或者修改来增加或者改变处理一个请求的那些职责。你可以将这种机制与静态的特例化处理对象的继承机制结合起来使用。

缺点:

    1. 不保证被接受 既然一个请求没有明确的接受者,那么就不能保证它一定会被处理——该请求可能一直在链对的末端都得不到处理。一个请求也可能因该链没有被正确配置而得不到处理。

9. 实现

下面是在责任链模式中要考虑的实现问题:

    1. 实现后继者链 有两种方法可以实现后继者链。
    • a) 定义新的链接(通常在Handler中定义,也可由ConcreteHandlers来定义)。

    • b) 使用已有的链接。

      我们的例子中定义了新的链接,但你常常可使用已有的对象引用来形成后继者链。例如,在一部分——整体层次结构中,父构件引用可定义一个部件的后继者。窗口组件(Widget)结构可能早已有这样的链接。

      当已有的链接能够支持你所需的链时,完全可以使用它们。这样你不需要明确定义链接,而且可以节省空间。但如果该结构不能反映应用所需的职责链,那么你必须定义额外的链接。

    1. 连接后继者 如果没有已有的引用可定义一个链,那么你必须自己引入它们。这种情况下Handler不仅定义该请求的接口,通常也维护后继链接(可以通过构造器实现)。这样Handler就提供了handleRequest的缺省实现:handleRequest向后继者(如果有的话)转发请求。如果ConcreteHandler子类对该请求不感兴趣,它不需要重定义转发操作,因为它的缺省实现时无条件转发。
  • 3)表示请求 可以有不同的方法表示请求。最简单的形式,比如在HandleHelp的例子中,请求是一个硬编码的(Hard-coded)操作调用。这种形式方便而且安全。但你只能转发Handler类定义的固定的一组请求。

    另一种选择是使用一个处理函数,这个函数通常以一个请求码(如一个整型常数或者一个字符串)为参数。这种方法支持请求数目不限。唯一的要求是发送方和接受方在请求如何编码问题上达成一致。

    这种方法更为灵活,但它需要用条件语句来区别请求代码以分派请求。另外,无法用类型安全的方法来传递请求参数,因此它们必须被手动打包和解包。显然,相对于直接调用一个操作来说它不太安全。

    为解决参数传递问题,我们可使用独立的请求对象来封装请求参数。Request类可明确地描述请求,而新类型的请求可用的它的子类来定义。这些子类可定义不同的请求参数。处理者必须知道请求的类型(即它们正使用哪一个)以访问这些参数。

    为标识请求,Request可定义一个访问器(accessor)函数返回该类的标识符。或者,如果实现语言支持的话,接受者可使用运行时的类型信息。

    不确定是不是这样。

    public class Test {
    	
    	public Pack handle(Pack pack) {
    		// ...... handle process
    		pack.doSomething();
    		return pack;
    	}
    }
    
    
    public interface Pack {
    	void doSomething();
    }
    
    
    public class DIYPack implements Pack{
    
    	@Override
    	public void doSomething() {
    		
    	}
    }
    
    1. 利用与语言特性 例如在SmallTalk语言中可以利用doesNotUnderstand机制转发请求,没有相应方法的消息被doesNotUnderstand的实现捕捉(trap in), 此实现可被重定义,从而可向一个对象的后继者转发该消息。这样就不需要手动实现转发;类仅处理他们它感兴趣的请求,而依赖doesNotUnderstand转发所有其他的请求.在Java有没有什么特性可以运用呢??

10. 代码示例

这里我使用的在自定义链接,且把Handler定义为接口,来处理不同类型的邮箱。

首先定义邮箱——Email.java(其实这里构造参数有四个,应该使用Buillder模式的)

public class Email {
	private String author;
	private EmailType type;
	private String content;
	private LocalDate date;
	
	public Email(String author, 
			EmailType type, 
			String content, 
			LocalDate date ) {
		this.author = author;
		this.type = type;
		this.content = content;
		this.date = date;
	}
	
	public String getAuthor() {
		return author;
	}
	public void setAuthor(String author) {
		this.author = author;
	}
	public EmailType getType() {
		return type;
	}
	public void setType(EmailType type) {
		this.type = type;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public LocalDate getDate() {
		return date;
	}
	public void setDate(LocalDate date) {
		this.date = date;
	}
	
}

接着是Handler——Handler.java

public interface Handler {
	
	void  nextHandler(Handler handler);
	
	void  handleEmail(Email email);
	
}

由于这里是想通过责任链处理邮箱,这里先定义邮箱的类型,使用的是枚举的方法。

EmailType.java

public enum EmailType {
	FANS_EMAIL,COMPLAIN_EMAIL,ADVICE_EMAIL,RUBBISH_EMAIL;
	// has an implicit empty constructor. Let's be explicit instead,
	// 枚举有一个其实也是类 自带空的默认构造器
}

接下来是四个ConcreteHandler——AdviceEmailHandler.java & ComplainEmailHandler.java & FanEmailHandler.java & RubbishEmailHandler.java

AdviceEmailHandler.java

public class AdviceEmailHandler implements Handler {
	
	private Handler nextHandler;
	private BusinessDepartment businessDepartment;
	
	public AdviceEmailHandler() {
		businessDepartment  = new BusinessDepartment();
	}
	
	@Override
	public void nextHandler(Handler nextHandler) {
		this.nextHandler = nextHandler;
	}
	
	@Override
	public void handleEmail(Email email) {
		if (email != null) {
			if (email.getType() == EmailType.ADVICE_EMAIL) {
				businessDepartment.readEmail(email);
			} 
			else this.nextHandler.handleEmail(email);
		}
	}
}

以及其辅助类 BusinessDepartment.java

public class BusinessDepartment {
	public void readEmail(Email email) {
		System.out.println("Business department read the email : " + 
				email.getContent());
	}
}

ComplainEmailHandler.java

public class ComplainEmailHandler implements Handler {
	
	private Handler nextHandler;
	private LawDepartment lawDepartment;
	
	public ComplainEmailHandler() {
		lawDepartment = new LawDepartment();
	}
	
	@Override
	public void nextHandler(Handler nextHandler) {
		this.nextHandler = nextHandler;
	}
	
	
	@Override
	public void handleEmail(Email email) {
		if (email != null) {
			if (email.getType() == EmailType.COMPLAIN_EMAIL) {
				lawDepartment.readEmail(email);
			}
			else this.nextHandler.handleEmail(email);
		}
	}
}


以及其辅助类 LawDepartment.java

public class LawDepartment {
	
	public void readEmail(Email email) {
		System.out.println("Law department read the Email :" + 
				email.getContent());
	}
}

FansEmail.java

public class FanEmailHandler implements Handler{
	
	private Handler nextHandler;
	private CEO ceo;
	
	public FanEmailHandler() {
		ceo = new CEO();
	}
	
	@Override
	public void nextHandler(Handler nextHandler) {
		this.nextHandler = nextHandler;
	}
	
	@Override
	public void handleEmail(Email email) {
		if (email != null) {
			if (email.getType() == EmailType.FANS_EMAIL) {
				ceo.readEmail(email);
			}
			else this.nextHandler.handleEmail(email);
		}
	}
}

以及其辅助类——CEO.java

public class CEO {
	
	public  void readEmail(Email email) {
		System.out.println("CEO read email : " + email.getContent());
	}
}

RubbishEmailHandler.java

public class RubbishEmailHandler implements Handler {
	
	private Handler nextHandler;

	
	@Override
	public void nextHandler(Handler nextHandler) {
		this.nextHandler = nextHandler;
	}
	
	@Override
	public void handleEmail(Email email) {
		if (email != null) {
			if (email.getType() == EmailType.RUBBISH_EMAIL) {
				ReStation.INSTANCE.recycleEmail(email);
			}
			else this.nextHandler.handleEmail(email);
		}		
	}	
}


值得一提的是,这里使用了Singleton模式,并且使用的是Joshua Bloch最推荐的Singleton实现的方式,采用枚举。下面是具体的实现代码

ReStation.java

public enum ReStation {
	INSTANCE;  // INSTANCE is same as 
			   //  public static final MySingleton INSTANCE = new MySingleton(); 
			   // 但更加简洁且无偿提供了序列化机制,即使在面对复杂的序列化或者反射攻击的时候
			   // 都能防止多次实例化。
		 
	private List<Email> emailList ;
	
	private ReStation() {
		emailList = new ArrayList<>();
	}
	
	public synchronized void recycleEmail(Email email) {
		System.out.println("Recycle station recycleing the email: " 
				+ email.getContent());
		emailList.add(email);
	}
	
	public synchronized void deleteEmail(Email email) {
		emailList.remove(email);
	}
	
	public synchronized void clear() {
		emailList.clear();
	}
}


这里为了最后一个无法的处理邮箱提供了一个缺省实现的Handler作为其继承者(successor)

NonHandler.java

public class NonHandler implements Handler {
	
	@Override
	public void nextHandler(Handler nextHandler) {
		
	}
	
	@Override
	public void handleEmail(Email email) {
		
	}	
}

接下里是Client——EmailListTestDrive.java

public class EmailListTestDrive {	
	
	private Handler handler1;
	
	private List<Email> emailList;
	
	public EmailListTestDrive(List<Email> emailList) {
		this.emailList = emailList;
		handler1 = new FanEmailHandler();
		Handler handler2 = new ComplainEmailHandler();
		Handler handler3 = new AdviceEmailHandler();
		Handler handler4 = new RubbishEmailHandler();
		Handler nonHandler = new NonHandler();
		handler1.nextHandler(handler2);
		handler2.nextHandler(handler3);
		handler3.nextHandler(handler4);
		handler4.nextHandler(nonHandler);
	}
	
	public void handleEmailList() {
		for (Email email : emailList) {
			handler1.handleEmail(email);
		}
	}
	
	
	
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<Email> emailList = new ArrayList<>();
		Email email1 = new Email("yanxun", EmailType.FANS_EMAIL, 
				"哇,这是我第一次使用自己的责任链模式",  LocalDate.now());
		Email email2 = new Email("wanzilin", EmailType.COMPLAIN_EMAIL, 
				"哇,我王者荣耀10颗星", LocalDate.of(2019, 3, 8));
		Email email3 = new Email("kobe", EmailType.ADVICE_EMAIL, 
				"Ok,your gumball is too sweet", LocalDate.of(2018, 8, 24));
		Email email4 = new Email("ルフィ", EmailType.RUBBISH_EMAIL, 
				"私はワンピースになりたい人です。", LocalDate.of(2017, 5, 1));
		
		emailList.add(email4);
		emailList.add(email3);
		emailList.add(email2);
		emailList.add(email1);
		EmailListTestDrive testDrive = new EmailListTestDrive(emailList);
		testDrive.handleEmailList();
	}

}

以及对于的测试结果:

Recycle station recycleing the email: 私はワンピースになりたい人です。
Business department read the email : Ok,your gumball is too sweet
Law department read the Email :哇,我王者荣耀10颗星
CEO read email : 哇,这是我第一次使用自己的责任链模式

最后附上类的UML图:

在这里插入图片描述

11. 已知应用

许多类库使用职责链模式处理用户事件,对Handler类它们使用不同的名字,但思想是一样的:当用户点击鼠标或者按下键盘,这个事件产生并沿链传播。

12. 相关模式

  • Composite模式: 责任链模式长与Composite模式一起使用。这种情况下,一个构件的父构件可作为它的后续。

13. 设计原则口袋

  • 封装变化
  • 针对接口编程,不针对实现编程
  • 多用组合,少用继承
  • 类应该对扩展开放,对修改关闭
  • 为交互对象间的松耦合设计而努力
  • 依赖抽象,不要依赖具体类
  • 只有密友进行交谈
  • 好莱坞原则——别来找我,我会来找你
  • 单一责任原则——类应该只有一个改变的理由

14. 参考文献

《设计模式:可复用型面向对象软件的基础》

《HeadFirst设计模式》

<think>我们正在讨论责任链模式Chain of Responsibility)。根据引用[1],责任链模式是一种行为设计模式,它通过将请求沿着处理者链传递来避免请求发送者和接收者之间的耦合。多个处理者对象都有机会处理请求,直到其中一个处理它为止。 引用[2]提供了一段示例代码,展示了责任链模式的实现方式。代码中定义了一个处理者基类CHandler,它包含一个指向下一个处理者的指针(m_pHandler)和一个处理请求的虚函数HandleRequest。然后定义了三个具体的处理者类(CConcreteHandler1, CConcreteHandler2, CConcreteHandler3),每个具体处理者根据自己的条件决定是否处理请求,如果不处理则传递给下一个处理者。 因此,我们可以这样介绍责任链模式责任链模式Chain of Responsibility)是一种行为设计模式,它允许你将请求沿着处理者链进行传递,直到有一个处理者能够处理它为止。这种模式解耦了请求的发送者和接收者,允许多个对象都有机会处理请求。 责任链模式的主要组成部分: 1. 处理者(Handler):定义一个处理请求的接口,通常包含一个指向下一个处理者的引用(后继者链接)。在示例代码中,CHandler就是抽象处理者。 2. 具体处理者(Concrete Handler):实现处理请求的具体逻辑。如果自己能够处理请求,则处理;否则将请求传递给后继者。示例代码中的CConcreteHandler1、CConcreteHandler2、CConcreteHandler3就是具体处理者。 责任链模式的特点: - 降低了对象之间的耦合度:请求发送者无需知道哪个对象会处理其请求。 - 增强了系统的可扩展性:可以动态地增加或修改处理者链。 - 增强了给对象指派职责的灵活性:通过改变链内的成员或者调整次序,可以动态地改变处理流程。 - 责任链简化了对象之间的连接:每个处理者只需保持一个指向其后继者的引用,而不需保持所有处理者的引用。 - 责任分担:每个处理者只需处理自己能够处理的请求,将不能处理的请求传递给下一个处理者,符合类的单一职责原则。 但是,责任链模式也有缺点: - 不能保证请求一定被处理:如果链中没有合适的处理者,请求可能到达链的末端也没有被处理。 - 对于较长的责任链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。 - 调试时可能比较复杂,特别是链条较长时,逻辑可能不易追踪。 应用场景: 责任链模式适用于以下情况: - 有多个对象可以处理同一个请求,具体哪个对象处理该请求在运行时自动确定。 - 需要动态指定一组对象处理请求,或者需要动态添加、删除处理者。 - 在不明确指定接收者的情况下,向多个对象中的一个提交请求。 下面我们用一段标准化的代码示例(使用C++)来展示责任链模式: ```cpp #include <iostream> // 抽象处理者 class Handler { public: virtual ~Handler() {} void SetNext(Handler* next) { next_ = next; } virtual void HandleRequest(int request) const = 0; protected: Handler* next_ = nullptr; }; // 具体处理者A class ConcreteHandlerA : public Handler { public: void HandleRequest(int request) const override { if (request < 10) { std::cout << "ConcreteHandlerA handled request " << request << "\n"; } else if (next_ != nullptr) { next_->HandleRequest(request); } } }; // 具体处理者B class ConcreteHandlerB : public Handler { public: void HandleRequest(int request) const override { if (request >= 10 && request < 20) { std::cout << "ConcreteHandlerB handled request " << request << "\n"; } else if (next_ != nullptr) { next_->HandleRequest(request); } } }; // 具体处理者C class ConcreteHandlerC : public Handler { public: void HandleRequest(int request) const override { if (request >= 20) { std::cout << "ConcreteHandlerC handled request " << request << "\n"; } else if (next_ != nullptr) { next_->HandleRequest(request); } } }; // 客户端代码 int main() { ConcreteHandlerA handlerA; ConcreteHandlerB handlerB; ConcreteHandlerC handlerC; handlerA.SetNext(&handlerB); handlerB.SetNext(&handlerC); // 创建一系列请求 int requests[] = {5, 15, 25}; for (int req : requests) { handlerA.HandleRequest(req); } return 0; } ``` 在这个示例中,我们创建了三个具体处理者(ConcreteHandlerA、ConcreteHandlerB、ConcreteHandlerC),它们分别处理不同范围的请求。客户端将请求发送给链中的第一个处理者(handlerA),然后请求沿着链传递直到被处理。 责任链模式在现实中的应用包括: - 事件处理系统(如GUI中的事件冒泡) - 日志记录器(不同级别的日志由不同的记录器处理) - Web框架中的中间件(每个中间件可以处理请求并决定是否传递给下一个) - 异常处理(try-catch块可以看作是一个责任链) 相关问题: 1. 责任链模式在哪些实际框架或库中有应用? 2. 如何处理责任链中请求未被处理的情况? 3. 责任链模式和状态模式有什么区别? 4. 如何动态地修改责任链的结构? </think>### 责任链模式Chain of Responsibility)介绍 责任链模式Chain of Responsibility)是一种**行为型设计模式**,核心思想是将多个处理对象连接成一条链,请求沿链传递直至被处理。这种模式解耦了请求发送者和接收者,允许多个对象都有机会处理请求。以下是详细解析: --- #### 一、核心结构与原理 1. **处理者(Handler)抽象类** 定义处理请求的接口,包含: - 指向下一个处理者的引用(`m_pHandler`) - 处理请求的虚方法(`HandleRequest()`) ```cpp class Handler { public: void SetNext(Handler* next) { m_pNext = next; } virtual void HandleRequest(int request) = 0; protected: Handler* m_pNext = nullptr; // 后继处理者 }; ``` 2. **具体处理者(Concrete Handler)** 实现具体的处理逻辑,若无法处理则转发给下一个处理者: ```cpp class ConcreteHandlerA : public Handler { public: void HandleRequest(int request) override { if (request < 10) std::cout << "HandlerA processed request: " << request << "\n"; else if (m_pNext) m_pNext->HandleRequest(request); // 传递给下一个 } }; ``` 3. **客户端调用** 构建处理链并发送请求: ```cpp int main() { Handler* handlerA = new ConcreteHandlerA(); Handler* handlerB = new ConcreteHandlerB(); handlerA->SetNext(handlerB); // 设置责任链 handlerA->HandleRequest(15); // 请求在链上传递 } ``` --- #### 二、关键特性 1. **解耦性** 请求发送者无需知道具体处理者,只需向链首提交请求[^1]。 2. **动态扩展** 可随时增减或调整处理者节点(如添加 `ConcreteHandlerC`)。 3. **处理灵活性** 请求可能被链中任意节点处理,也可能未被任何节点处理。 4. **符合开闭原则** 新增处理者无需修改现有代码。 --- #### 三、适用场景 1. **多级请求处理** 如审批流程(员工→经理→CEO)、日志级别过滤(DEBUG→INFO→ERROR)。 2. **动态指定处理对象** 例如中间件管道(如Express.js的中间件链)。 3. **未知接收者的场景** 如GUI事件冒泡机制(控件→父容器→窗口)。 --- #### 四、优缺点分析 | **优点** | **缺点** | |-----------------------------------|--------------------------| | 降低耦合度(请求发送者与接收者分离) | 请求可能未被处理(链尾无兜底) | | 动态增删处理节点 | 长链可能影响性能 | | 简化对象间连接(单向链式传递) | 调试复杂度较高 | --- #### 五、与其他模式的关系 - **与装饰器模式** 两者都基于链式结构,但装饰器模式重在**增强功能**,责任链重在**分发请求**。 - **与状态模式** 状态模式通过状态转移改变行为,责任链通过固定链条传递请求。 --- ### 相关问题 1. 责任链模式如何处理请求未被任何节点处理的情况? 2. 在实际框架(如Spring、Express.js)中责任链模式有哪些典型应用? 3. 如何在责任链模式中实现请求的优先级处理? 4. 责任链模式与命令模式结合会产生什么效果? [^1]: 责任链模式通过对象链解耦请求发送者和接收者[^1]。 [^2]: 具体处理者通过条件判断决定是否处理请求[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值