中介者模式

中介者模式 | 菜鸟教程 https://www.runoob.com/design-pattern/mediator-pattern.html

中介者模式

中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。

介绍

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

主要解决:对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。

何时使用:多个类相互耦合,形成了网状结构。

如何解决:将上述网状结构分离为星型结构。

关键代码:对象 Colleague 之间的通信封装到一个类中单独处理。

应用实例: 1、中国加入 WTO 之前是各个国家相互贸易,结构复杂,现在是各个国家通过 WTO 来互相贸易。 2、机场调度系统。 3、MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。

优点: 1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。

缺点:中介者会庞大,变得复杂难以维护。

使用场景: 1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。 2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。

注意事项:不应当在职责混乱的时候使用。

实现

我们通过聊天室实例来演示中介者模式。实例中,多个用户可以向聊天室发送消息,聊天室向所有的用户显示消息。我们将创建两个类 ChatRoom 和 UserUser 对象使用 ChatRoom 方法来分享他们的消息。

MediatorPatternDemo,我们的演示类使用 User 对象来显示他们之间的通信。

中介者模式的 UML 图

步骤 1

创建中介类。

ChatRoom.java

importjava.util.Date; publicclassChatRoom{publicstaticvoidshowMessage(Useruser, Stringmessage){System.out.println(newDate().toString() + " [" + user.getName() +"] : " + message); }}

步骤 2

创建 user 类。

User.java

publicclassUser{privateStringname; publicStringgetName(){returnname; }publicvoidsetName(Stringname){this.name = name; }publicUser(Stringname){this.name = name; }publicvoidsendMessage(Stringmessage){ChatRoom.showMessage(this,message); }}

步骤 3

使用 User 对象来显示他们之间的通信。

MediatorPatternDemo.java

publicclassMediatorPatternDemo{publicstaticvoidmain(String[]args){Userrobert = newUser("Robert"); Userjohn = newUser("John"); robert.sendMessage("Hi! John!"); john.sendMessage("Hello! Robert!"); }}

步骤 4

执行程序,输出结果:

ThuJan3116:05:46 IST 2013[Robert]:Hi!John!ThuJan3116:05:46 IST 2013[John]:Hello!Robert!

 

Mediator Pattern | Object Oriented Design https://www.oodesign.com/mediator-pattern.html

 

Mediator Pattern


Motivation

In order to have a good object oriented design we have to create lots of classes interacting one with each other. If certain principles are not applied the final framework will end in a total mess where each object relies on many other objects in order to run. In order to avoid tight coupled frameworks, we need a mechanism to facilitate the interaction between objects in a manner in that objects are not aware of the existence of other objects.

Let's take the example of a screen. When we create it we add all sort of controls to the screen. This control need to interact with all the other control. For example when a button is pressed it must know if the data is valid in other controls. As you have seen if you created different applications using forms you don't have to modify each control class each time you add a new control to the form. All the operations between controls are managed by the form class itself. This class is called mediator.

Intent

Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.

 Mediator Implementation - UML Class Diagram

Implementation


Participants

The participants classes in this pattern are:
  • Mediator - defines an interface for communicating with Colleague objects.
  • ConcreteMediator - knows the colleague classes and keep a reference to the colleague objects.
  • - implements the communication and transfer the messages between the colleague classes
  • Colleague classes - keep a reference to its Mediator object
  • - communicates with the Mediator whenever it would have otherwise communicated with another Colleague.


Applicability

According to Gamma et al, the Mediator pattern should be used when:
  • a set of objects communicate in well-defined but complex ways. The resulting interdependencies are unstructured and difficult to understand.
  • reusing an object is difficult because it refers to and communicates with many other objects.
  • a behavior that's distributed between several classes should be customizable without a lot of subclassing.

Examples

Example 1 - GUI Libraries

The mediator example is one pattern that is already used in many applications. One of the examples is represented by the Dialog classes in GUI applications frameworks. A Dialog window is a collection of graphic and non-graphic controls. The Dialog class provides the mechanism to facilitate the interaction between controls. For example, when a new value is selected from a ComboBox object a Label has to display a new value. Both the ComboBox and the Label are not aware of each other structure and all the interaction is managed by the Dialog object. Each control is not aware of the existence of other controls.

Example 2 - Chat application

The chat application is another example of the mediator pattern. In a chat application we can have several participants. It's not a good idea to connect each participant to all the others because the number of connections would be really high, there would be technical problems due to proxies and firewalls, etc... . The most appropriate solution is to have a hub where all participants will connect; this hub is just the mediator class.

Participants:
  • Chatroom(Mediator) - Defines the interface for interacting with participants
  • ChatroomImpl (ConcreteMediator) - implements the operations defined by the Chatroom interface. The operations are managing the interactions between the objects: when one participant sends a message, the message is sent to the other participants.
  • Participant(Collegue) - defines an interface for the participants.
  • HumanParticipant, Bot (ConcreteCollegue) - implements participants; the participant can be a human or a bot, each one having a distinct implementation but implementing the same interface. Each participant will keep only a reference to the mediator.

Specific problems and implementation

Abstract Mediators

There is no need to create an Abstract Mediator class or an interface as long as the colleagues are going to use only one mediator. The definition of an abstract Mediator is required only if the colleagues needs to work with different mediators.

Communication between mediators and colleagues

    There are different ways to realize the communication between the colleagues and its mediator:
  • One of the most used methods is to use the Observer pattern. The mediator can be also an observer and the Colleagues can be implement an observable object. Each time an change is made in the state of the observable object, the observer(mediator) gets notified and it notify all other colleagues objects.
  • Alternative methods can be used to send messages to the mediator. For example a simple delegation can be used and specialised methods can be exposed by the mediator
  • In more complex implementations asynchronous messages can be added to to a message queue, from where they can be picked up by the mediator object

Complexity of Mediator object

The mediator object handles all the interaction between the participants objects. One potential problem is complexity of the mediator when the number of participants is a high and the different participant classes is high. If you created custom dialogs for GUI applications you remember that after some time the dialogs classes become really complex because they had to manage a lot of operations.


Consequences

As with most design patterns, there are both advantages and disadvantages of using the Mediator Patern. The following section will briefly outline a few of these issues.

Advantages

  • Comprehension - The mediator encapsulate the logic of mediation between the colleagues. From this reason it' more easier to understand this logic since it is kept in only one class.
  • Decoupled Colleagues - The colleague classes are totally decoupled. Adding a new colleague class is very easy due to this decoupling level.
  • Simplified object protocols - The colleague objects need to communicate only with the mediator objects. Practically the mediator pattern reduce the required communication channels(protocols) from many to many to one to many and many to one.
  • Limits Subclassing - Because the entire communication logic is encapsulated by the mediator class, when this logic need to be extended only the mediator class need to be extended.

Disadvantages

  • Complexity - in practice the mediators tends to become more complex and complex. A good practice is to take care to make the mediator classes responsible only for the communication part. For example when implementing different screens the the screen class should not contain code which is not a part of the screen operations. It should be put in some other classes.

Related Patterns

There are a few design patterns that are closely related to the Mediator pattern and are often used in conjunction with it.
  • Facade Pattern - a simplified mediator becomes a facade pattern if the mediator is the only active class and the colleagues are passive classes. A facade pattern is just an implementation of the mediator pattern where mediator is the only object triggering and invoking actions on passive colleague classes. The Facade is being call by some external classes.
  • Adapter Pattern - the mediator patter just "mediate" the requests between the colleague classes. It is not supposed to change the messages it receives and sends; if it alters those messages then it is an Adapter pattern.
  • Observer Pattern - the observer and mediator are similar patterns, solving the same problem. The main difference between them is the problem they address. The observer pattern handles the communication between observers and subjects or subject. It's very probable to have new observable objects added. On the other side in the mediator pattern the mediator class is the the most likely class to be inherited.

Known Uses

In the following section, we'll discuss some real-world uses of the Mediator pattern. You'll find the Mediator in many situations where there are many components that must interact with one another in complex ways. User Interfaces

Maybe the mediator pattern is mostly used in the user interfaces. Almost any GUI framework is build around it. Like discussed before, the classes representing forms (Dialog, Form,... ) represents the the mediator while each control represents a colleague. The form class provides the mechanism to facilitate the interaction between controls; an inherited class is created each time a new screen is created and the specific code is written in this class. This way, the controls communication is mediated by this form class. Java Message Service

The Java Message Service (JMS) API is a Java Message Oriented Middleware (MOM) API for sending messages between two or more clients. The JMS API supports 2 models. One of them is the publish-subscribe model. It is an implementation of the mediator pattern. The messages can be publisehd based on a particular topic. The publisher has to create a subscription to which different subscribers may subscribe. Zero or more subscribers may subscribe to receive messages on a particular message topic. The publisher and the subscriber doesn't know one about eachother, the subscriber can be even inactive. In this case the subscriber receives the messages when it will become active.


Conclusion

The mediator pattern is used to takes the role of a hub or router and facilitates the communication between many classes. A similarity can be made with the database systems. The mediator transform a hard to implement relation of many to many, where each calls has to communicate with each other class, in 2 relations, easy to implement, of many to one and one to many, where the communication is handled by the mediator class.

 

2. 中介者模式 — Graphic Design Patterns https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/mediator.html

2. 中介者模式

2.1. 模式动机

  • 在用户与用户直接聊天的设计方案中,用户对象之间存在很强的关联性,将导致系统出现如下问题:
  • 系统结构复杂:对象之间存在大量的相互关联和调用,若有一个对象发生变化,则需要跟踪和该对象关联的其他所有对象,并进行适当处理。
  • 对象可重用性差:由于一个对象和其他对象具有很强的关联,若没有其他对象的支持,一个对象很难被另一个系统或模块重用,这些对象表现出来更像一个不可分割的整体,职责较为混乱。
  • 系统扩展性低:增加一个新的对象需要在原有相关对象上增加引用,增加新的引用关系也需要调整原有对象,系统耦合度很高,对象操作很不灵活,扩展性差。
  • 在面向对象的软件设计与开发过程中,根据“单一职责原则”,我们应该尽量将对象细化,使其只负责或呈现单一的职责。
  • 对于一个模块,可能由很多对象构成,而且这些对象之间可能存在相互的引用,为了减少对象两两之间复杂的引用关系,使之成为一个松耦合的系统,我们需要使用中介者模式,这就是中介者模式的模式动机。

2.2. 模式定义

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

2.3. 模式结构

中介者模式包含如下角色:

  • Mediator: 抽象中介者
  • ConcreteMediator: 具体中介者
  • Colleague: 抽象同事类
  • ConcreteColleague: 具体同事类
../_images/Mediator.jpg

2.5. 代码分析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include "ConcreteColleagueA.h"
#include "ConcreteMediator.h"
#include "ConcreteColleagueB.h"

using namespace std;

int main(int argc, char *argv[])
{
	ConcreteColleagueA * pa = new ConcreteColleagueA();
	ConcreteColleagueB * pb = new ConcreteColleagueB();
	ConcreteMediator * pm = new ConcreteMediator();
	pm->registered(1,pa);
	pm->registered(2,pb);
	// sendmsg from a to b
	pa->sendmsg(2,"hello,i am a");
	// sendmsg from b to a
	pb->sendmsg(1,"hello,i am b");
	
	delete pa,pb,pm;
	return 0;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
///////////////////////////////////////////////////////////
//  ConcreteMediator.h
//  Implementation of the Class ConcreteMediator
//  Created on:      07-十月-2014 21:30:47
//  Original author: colin
///////////////////////////////////////////////////////////

#if !defined(EA_8CECE546_61DD_456f_A3E7_D98BC078D8E8__INCLUDED_)
#define EA_8CECE546_61DD_456f_A3E7_D98BC078D8E8__INCLUDED_

#include "ConcreteColleagueB.h"
#include "Mediator.h"
#include "ConcreteColleagueA.h"
#include <map>
using namespace std;
class ConcreteMediator : public Mediator
{

public:
	ConcreteMediator();
	virtual ~ConcreteMediator();

	virtual void operation(int nWho,string str);
	virtual void registered(int nWho, Colleague * aColleague);
private:
	map<int,Colleague*> m_mpColleague;
};
#endif // !defined(EA_8CECE546_61DD_456f_A3E7_D98BC078D8E8__INCLUDED_)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
///////////////////////////////////////////////////////////
//  ConcreteMediator.cpp
//  Implementation of the Class ConcreteMediator
//  Created on:      07-十月-2014 21:30:48
//  Original author: colin
///////////////////////////////////////////////////////////

#include "ConcreteMediator.h"
#include <map>
#include <iostream>
using namespace std;

ConcreteMediator::ConcreteMediator(){

}

ConcreteMediator::~ConcreteMediator(){

}

void ConcreteMediator::operation(int nWho,string str){
	map<int,Colleague*>::const_iterator itr = m_mpColleague.find(nWho);
	if(itr == m_mpColleague.end())
	{
		cout << "not found this colleague!" << endl;
		return;
	}
	Colleague* pc = itr->second;
	pc->receivemsg(str);
}
void ConcreteMediator::registered(int nWho,Colleague * aColleague){
	map<int,Colleague*>::const_iterator itr = m_mpColleague.find(nWho);
	if(itr == m_mpColleague.end())
	{
		m_mpColleague.insert(make_pair(nWho,aColleague));
		//同时将中介类暴露给colleague 
		aColleague->setMediator(this);
	}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
///////////////////////////////////////////////////////////
//  ConcreteColleagueA.h
//  Implementation of the Class ConcreteColleagueA
//  Created on:      07-十月-2014 21:30:47
//  Original author: colin
///////////////////////////////////////////////////////////

#if !defined(EA_79979DD4_1E73_46db_A635_E3F516ACCE0A__INCLUDED_)
#define EA_79979DD4_1E73_46db_A635_E3F516ACCE0A__INCLUDED_

#include "Colleague.h"

class ConcreteColleagueA : public Colleague
{

public:
	ConcreteColleagueA();
	virtual ~ConcreteColleagueA();

	virtual void sendmsg(int toWho,string str);
	virtual void receivemsg(string str);

};
#endif // !defined(EA_79979DD4_1E73_46db_A635_E3F516ACCE0A__INCLUDED_)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
///////////////////////////////////////////////////////////
//  ConcreteColleagueA.cpp
//  Implementation of the Class ConcreteColleagueA
//  Created on:      07-十月-2014 21:30:47
//  Original author: colin
///////////////////////////////////////////////////////////

#include "ConcreteColleagueA.h"
#include <iostream>
using namespace std;

ConcreteColleagueA::ConcreteColleagueA(){
}

ConcreteColleagueA::~ConcreteColleagueA(){
}

void ConcreteColleagueA::sendmsg(int toWho,string str){
	cout << "send msg from colleagueA,to:" << toWho << endl;
	m_pMediator->operation(toWho,str);
}
void ConcreteColleagueA::receivemsg(string str){
	cout << "ConcreteColleagueA reveivemsg:" << str <<endl;
}

运行结果:

../_images/Mediator_run.jpg

2.6. 模式分析

中介者模式可以使对象之间的关系数量急剧减少。

中介者承担两方面的职责:

  • 中转作用(结构性):通过中介者提供的中转作用,各个同事对象就不再需要显式引用其他同事,当需要和其他同事进行通信时,通过中介者即可。该中转作用属于中介者在结构上的支持。
  • 协调作用(行为性):中介者可以更进一步的对同事之间的关系进行封装,同事可以一致地和中介者进行交互,而不需要指明中介者需要具体怎么做,中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。该协调作用属于中介者在行为上的支持。
../_images/Mediator_eg.jpg

时序图

../_images/seq_Mediator_eg.jpg

2.7. 实例

实例:虚拟聊天室

某论坛系统欲增加一个虚拟聊天室,允许论坛会员通过该聊天室进行信息交流,普通会员(CommonMember)可以给其他会员发送文本信息,钻石会员(DiamondMember)既可以给其他会员发送文本信息,还可以发送图片信息。该聊天室可以对不雅字符进行过滤,如“日”等字符;还可以对发送的图片大小进行控制。用中介者模式设计该虚拟聊天室。

2.8. 优点

中介者模式的优点

  • 简化了对象之间的交互。
  • 将各同事解耦。
  • 减少子类生成。
  • 可以简化各同事类的设计和实现。

2.9. 缺点

中介者模式的缺点

  • 在具体中介者类中包含了同事之间的交互细节,可能会导致具体中介者类非常复杂,使得系统难以维护。

2.10. 适用环境

在以下情况下可以使用中介者模式:

  • 系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解。
  • 一个对象由于引用了其他很多对象并且直接和这些对象通信,导致难以复用该对象。
  • 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。可以通过引入中介者类来实现,在中介者中定义对象。
  • 交互的公共行为,如果需要改变行为则可以增加新的中介者类。

2.11. 模式应用

MVC架构中控制器

Controller 作为一种中介者,它负责控制视图对象View和模型对象Model之间的交互。如在Struts中,Action就可以作为JSP页面与业务对象之间的中介者。

2.12. 模式扩展

中介者模式与迪米特法则

  • 在中介者模式中,通过创造出一个中介者对象,将系统中有关的对象所引用的其他对象数目减少到最少,使得一个对象与其同事之间的相互作用被这个对象与中介者对象之间的相互作用所取代。因此,中介者模式就是迪米特法则的一个典型应用。

中介者模式与GUI开发

  • 中介者模式可以方便地应用于图形界面(GUI)开发中,在比较复杂的界面中可能存在多个界面组件之间的交互关系。
  • 对于这些复杂的交互关系,有时候我们可以引入一个中介者类,将这些交互的组件作为具体的同事类,将它们之间的引用和控制关系交由中介者负责,在一定程度上简化系统的交互,这也是中介者模式的常见应用之一。

2.13. 总结

  • 中介者模式用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。中介者模式又称为调停者模式,它是一种对象行为型模式。
  • 中介者模式包含四个角色:抽象中介者用于定义一个接口,该接口用于与各同事对象之间的通信;具体中介者是抽象中介者的子类,通过协调各个同事对象来实现协作行为,了解并维护它的各个同事对象的引用;抽象同事类定义各同事的公有方法;具体同事类是抽象同事类的子类,每一个同事对象都引用一个中介者对象;每一个同事对象在需要和其他同事对象通信时,先与中介者通信,通过中介者来间接完成与其他同事类的通信;在具体同事类中实现了在抽象同事类中定义的方法。
  • 通过引入中介者对象,可以将系统的网状结构变成以中介者为中心的星形结构,中介者承担了中转作用和协调作用。中介者类是中介者模式的核心,它对整个系统进行控制和协调,简化了对象之间的交互,还可以对对象间的交互进行进一步的控制。
  • 中介者模式的主要优点在于简化了对象之间的交互,将各同事解耦,还可以减少子类生成,对于复杂的对象之间的交互,通过引入中介者,可以简化各同事类的设计和实现;中介者模式主要缺点在于具体中介者类中包含了同事之间的交互细节,可能会导致具体中介者类非常复杂,使得系统难以维护。
  • 中介者模式适用情况包括:系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解;一个对象由于引用了其他很多对象并且直接和这些对象通信,导致难以复用该对象;想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。

 

 

 

 

 

 

 

 

 

 

中介者模式

中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。

介绍

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

主要解决:对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。

何时使用:多个类相互耦合,形成了网状结构。

如何解决:将上述网状结构分离为星型结构。

关键代码:对象 Colleague 之间的通信封装到一个类中单独处理。

应用实例: 1、中国加入 WTO 之前是各个国家相互贸易,结构复杂,现在是各个国家通过 WTO 来互相贸易。 2、机场调度系统。 3、MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。

优点: 1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。

缺点:中介者会庞大,变得复杂难以维护。

使用场景: 1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。 2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。

注意事项:不应当在职责混乱的时候使用。

实现

我们通过聊天室实例来演示中介者模式。实例中,多个用户可以向聊天室发送消息,聊天室向所有的用户显示消息。我们将创建两个类 ChatRoom 和 UserUser 对象使用 ChatRoom 方法来分享他们的消息。

MediatorPatternDemo,我们的演示类使用 User 对象来显示他们之间的通信。

中介者模式的 UML 图

步骤 1

创建中介类。

ChatRoom.java

importjava.util.Date; publicclassChatRoom{publicstaticvoidshowMessage(Useruser, Stringmessage){System.out.println(newDate().toString() + " [" + user.getName() +"] : " + message); }}

步骤 2

创建 user 类。

User.java

publicclassUser{privateStringname; publicStringgetName(){returnname; }publicvoidsetName(Stringname){this.name = name; }publicUser(Stringname){this.name = name; }publicvoidsendMessage(Stringmessage){ChatRoom.showMessage(this,message); }}

步骤 3

使用 User 对象来显示他们之间的通信。

MediatorPatternDemo.java

publicclassMediatorPatternDemo{publicstaticvoidmain(String[]args){Userrobert = newUser("Robert"); Userjohn = newUser("John"); robert.sendMessage("Hi! John!"); john.sendMessage("Hello! Robert!"); }}

步骤 4

执行程序,输出结果:

ThuJan3116:05:46 IST 2013[Robert]:Hi!John!ThuJan3116:05:46 IST 2013[John]:Hello!Robert!
posted @ 2021-05-07 13:44  papering  阅读(114)  评论(0编辑  收藏  举报