设计模式:访问者模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)
上一篇《状态模式》 下一篇《原型模式》
简介:
访问者模式,它是一种将数据操作与数据结构分离的设计模式,它属于行为型模式。访问者模式的基本思想是,针对系统中拥有固定类型数的对象结构(元素),在其内提供一个accept()方法用来接受访问者对象的访问。不同的访问者对同一元素的访问内容不同,使得相同的元素集合可以产生不同的数据结果。accept()方法可以接收不同的访问者对象,然后在内部将自己(元素)转发到接收到的访问者对象的visit()方法内。访问者内部对应类型的visit()方法就会得到回调执行,对元素进行操作。
访问者模式的使用场景:
1、操作复杂且频繁的数据结构:当需要对一个复杂且频繁变化的数据结构进行操作时,如遍历、查找、统计等,使用访问者模式可以提高代码的可维护性和可复用性。
2、数据结构需要频繁修改:如果数据结构需要频繁修改,而操作又需要与数据结构保持相对独立,这时可以使用访问者模式。通过将操作封装在访问者类中,可以在不修改元素类的情况下增加新的操作,降低了元素类和操作之间的耦合度。
3、需要对数据结构进行复杂的操作:当需要对数据结构进行复杂的操作时,如需要对元素进行自定义的访问、更新、删除等操作,使用访问者模式可以将这些操作封装在访问者类中,使代码更加清晰和易于维护。
4、需要对数据结构进行自定义的查询:当需要对数据结构进行自定义的查询时,如需要根据特定条件对元素进行筛选、排序等操作,使用访问者模式可以将查询逻辑封装在访问者类中,使代码更加灵活和可扩展。
访问者模式的创建步骤:
1、定义元素类:首先需要定义元素类,这些元素类构成了数据结构的基本单元。元素类通常具有一些公共的方法,如accept(),用于接受访问者的访问。
2、定义访问者接口:然后需要定义访问者接口,这个接口为元素类定义了访问的方法。访问者接口通常具有一个visit()方法,用于访问元素对象。
3、实现访问者类:接着需要实现访问者接口,创建访问者类。访问者类通常具有visit()方法的具体实现,用于对元素对象进行操作。
实现元素类的accept()方法:在元素类中实现accept()方法,这个方法接受一个访问者对象作为参数,并在内部将自己转发到接收到的访问者对象的visit()方法内。
4、使用访问者模式:最后,可以通过创建元素对象和访问者对象,并调用元素对象的accept()方法来使用访问者模式。accept()方法会将自己转发到访问者对象的visit()方法内,从而实现对元素对象的操作。
访问者模式的优点,主要包括:
1、扩展性好:访问者模式使得增加新的操作变得很容易,只需要添加一个新的访问者类即可。这提高了系统的可扩展性和可维护性。
2、复用性好:访问者模式可以将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。这提高了系统的复用程度,并且可以避免在每个元素类中都重复实现相同的行为。
3、灵活性好:访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。这使得系统更加灵活,能够适应不同的需求和变化。
4、符合单一职责原则:访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。这符合单一职责原则,使得代码更加清晰和易于维护。
5、便于积累状态:每一个单独的访问者对象都集中了相关的行为,从而也就可以在访问的过程中将执行操作的状态积累在自己内部,而不是分散到很多的节点对象中。这有利于系统状态的积累和管理。
访问者模式的缺点,主要包括:
1、破坏封装:访问者模式要求访问者对象访问并调用每一个节点对象的操作,这隐含了一个对所有节点对象的要求:它们必须暴露一些自己的操作和内部状态。这破坏了对象的封装性。
2、违反了依赖倒置原则:访问者模式依赖了具体类,而没有依赖抽象类。这违反了依赖倒置原则,使得代码的维护和扩展性降低。
3、增加新的节点类变得困难:每增加一个新的节点都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作。这使得系统的扩展性降低。
4、实现复杂:访问者模式需要定义访问者接口和实现访问者类,这会增加代码的复杂度和维护成本。同时,使用访问者模式需要对数据结构进行修改,这也会增加实现的难度和复杂度。
总之,访问者模式虽然可以提高系统的可扩展性和灵活性,但也存在一些缺点,需要在设计时权衡考虑。
示例:
一、C#访问者模式
以下是一个示例,展示了如何在C#中实现访问者模式:
//首先定义一个访问者接口:
public interface IVisitor
{
void Visit(ConcreteElement1 element);
void Visit(ConcreteElement2 element);
}
//然后定义一个抽象元素类和具体元素类:
public abstract class Element
{
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}
public class ConcreteElement1 : Element
{
public void SomeOperation1()
{
Console.WriteLine("ConcreteElement1 operation");
}
}
public class ConcreteElement2 : Element
{
public void SomeOperation2()
{
Console.WriteLine("ConcreteElement2 operation");
}
}
//接下来定义一个访问者类:
public class Visitor : IVisitor
{
public void Visit(ConcreteElement1 element)
{
element.SomeOperation1();
}
public void Visit(ConcreteElement2 element)
{
element.SomeOperation2();
}
}
//最后在客户端代码中使用访问者模式:
public static void Main(string[] args)
{
ConcreteElement1 element1 = new ConcreteElement1();
ConcreteElement2 element2 = new ConcreteElement2();
Visitor visitor = new Visitor();
element1.Accept(visitor); // 调用操作1
element2.Accept(visitor); // 调用操作2
}
二、java访问者模式
访问者模式通常通过以下方式实现:
//定义元素接口(Element Interface)
public interface Element {
void accept(Visitor visitor);
}
//定义访问者接口(Visitor Interface)
public interface Visitor {
void visit(ConcreteElement1 element);
void visit(ConcreteElement2 element);
}
//创建元素类(Concrete Element Classes)
public class ConcreteElement1 implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class ConcreteElement2 implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
//创建访问者类(Concrete Visitor Classes)
public class Visitor implements Visitor {
@Override
public void visit(ConcreteElement1 element) {
// 对元素对象进行操作
}
@Override
public void visit(ConcreteElement2 element) {
// 对元素对象进行操作
}
}
//在客户端代码中使用访问者模式
public static void main(String[] args) {
ConcreteElement1 element1 = new ConcreteElement1();
ConcreteElement2 element2 = new ConcreteElement2();
Visitor visitor = new Visitor();
element1.accept(visitor); // 调用操作1
element2.accept(visitor); // 调用操作2
}
三、javascript访问者模式
在JavaScript中,访问者实现方式如下:
//定义元素接口(Element Interface)
const Element = {
accept: function(visitor) {
visitor.visit(this);
}
};
//定义访问者接口(Visitor Interface)
const Visitor = {
visit: function(element) {
// 对元素对象进行操作
}
};
//创建元素类(Concrete Element Classes)
class ConcreteElement1 extends Element {
constructor() {
super();
}
}
class ConcreteElement2 extends Element {
constructor() {
super();
}
}
//创建访问者类(Concrete Visitor Classes)
class Visitor1 extends Visitor {
visit(element) {
// 对元素对象进行操作1
}
}
class Visitor2 extends Visitor {
visit(element) {
// 对元素对象进行操作2
}
}
//在客户端代码中使用访问者模式
const element1 = new ConcreteElement1();
const element2 = new ConcreteElement2();
const visitor1 = new Visitor1();
const visitor2 = new Visitor2();
element1.accept(visitor1); // 调用操作1
element2.accept(visitor2); // 调用操作2
四、C++访问者模式
以下是在C++中实现访问者模式:
//定义元素接口(Element Interface)
class Element {
public:
virtual void accept(Visitor* visitor) = 0;
};
//定义访问者接口(Visitor Interface)
class Visitor {
public:
virtual void visit(ConcreteElement1* element) = 0;
virtual void visit(ConcreteElement2* element) = 0;
};
//创建元素类(Concrete Element Classes)
class ConcreteElement1 : public Element {
public:
void accept(Visitor* visitor) override {
visitor->visit(this);
}
};
class ConcreteElement2 : public Element {
public:
void accept(Visitor* visitor) override {
visitor->visit(this);
}
};
//创建访问者类(Concrete Visitor Classes)
class Visitor1 : public Visitor {
public:
void visit(ConcreteElement1* element) override {
// 对元素对象进行操作1
}
};
class Visitor2 : public Visitor {
public:
void visit(ConcreteElement2* element) override {
// 对元素对象进行操作2
}
};
//在客户端代码中使用访问者模式
int main() {
ConcreteElement1* element1 = new ConcreteElement1();
ConcreteElement2* element2 = new ConcreteElement2();
Visitor* visitor1 = new Visitor1();
Visitor* visitor2 = new Visitor2();
element1->accept(visitor1); // 调用操作1
element2->accept(visitor2); // 调用操作2
}
五、python访问者模式
以下是在python中实现访问者模式:
//定义元素接口(Element Interface)
from abc import ABC, abstractmethod
class Element(ABC):
@abstractmethod
def accept(self, visitor):
pass
//定义访问者接口(Visitor Interface)
class Visitor(ABC):
@abstractmethod
def visit(self, element):
pass
//创建元素类(Concrete Element Classes)
class ConcreteElement1(Element):
def accept(self, visitor):
visitor.visit(self)
class ConcreteElement2(Element):
def accept(self, visitor):
visitor.visit(self)
//创建访问者类(Concrete Visitor Classes)
class Visitor1(Visitor):
def visit(self, element):
# 对元素对象进行操作1
pass
class Visitor2(Visitor):
def visit(self, element):
# 对元素对象进行操作2
pass
//在客户端代码中使用访问者模式
if __name__ == '__main__':
element1 = ConcreteElement1()
element2 = ConcreteElement2()
visitor1 = Visitor1()
visitor2 = Visitor2()
element1.accept(visitor1) # 调用操作1
element2.accept(visitor2) # 调用操作2
六、go访问者模式
以下是一个示例,展示了如何在go中实现访问者模式:
//首先,定义一个接口,表示元素:
type Element interface {
Accept(Visitor)
}
//然后,定义一个接口,表示访问者:
type Visitor interface {
Visit(Element)
}
//接下来,创建一些具体的元素类和访问者类:
type ConcreteElement1 struct{}
func (e *ConcreteElement1) Accept(v Visitor) {
v.Visit(*e)
}
type ConcreteElement2 struct{}
func (e *ConcreteElement2) Accept(v Visitor) {
v.Visit(*e)
}
type ConcreteVisitor struct{}
func (v *ConcreteVisitor) Visit(e Element) {
// 对元素进行操作的具体实现
fmt.Println("Visiting ConcreteElement1 or ConcreteElement2")
}
//最后,在客户端代码中使用访问者模式:
func main() {
element1 := &ConcreteElement1{}
element2 := &ConcreteElement2{}
visitor := &ConcreteVisitor{}
element1.Accept(visitor) // 调用操作1
element2.Accept(visitor) // 调用操作2
}
七、PHP访问者模式
以下是一个示例,展示了如何在PHP中实现访问者模式:
//首先,定义一个接口,表示元素:
interface Element {
public function accept(Visitor $visitor);
}
//然后,定义一个接口,表示访问者:
interface Visitor {
public function visit(Element $element);
}
//接下来,创建一些具体的元素类和访问者类:
class ConcreteElement implements Element {
private $data;
public function __construct($data) {
$this->data = $data;
}
public function accept(Visitor $visitor) {
$visitor->visit($this);
}
}
class ConcreteVisitor implements Visitor {
public function visit(Element $element) {
// 对元素进行操作的具体实现,例如:
echo "Visiting element: " . $element->data . "\n";
}
}
//最后,在客户端代码中使用访问者模式:
$element1 = new ConcreteElement("Element 1");
$element2 = new ConcreteElement("Element 2");
$visitor = new ConcreteVisitor();
$element1->accept($visitor); // 调用操作1
$element2->accept($visitor); // 调用操作2
《完结》
上一篇《状态模式》 下一篇《原型模式》