C#デザインパターンカタログ GoFの23パターン

GoFデザインパターン23種類をC#で書いたまとめ記事です。

本記事はGang of Fourの「オブジェクト指向における再利用のためのデザインパターン」で紹介されている23種類のデザインパターンを参考に、最小構成のC#サンプルコードを列挙しています。 どのデザインパターンを適用するか悩んだ際など、自由にご利用ください。

なお免責として、自分の理解の範囲なので悪しからず。内容に関する指摘は歓迎です。

デザインパターンと直接関係していないクラスや関数・変数に関しては、Some~, Hoge, Foo等の言葉を用いて表しています。

http://amazon.co.jp/オブジェクト指向における再利用のためのデザインパターン-エリック-ガンマ/dp/4797311126amazon.co.jp

GitHubで見たい方はこちら

Abstract Factory

Factory

public abstract class AbstractFactory
{
    public abstract Product CreateProduct();
}


public class ConcreteFactory1 : AbstractFactory
{
    public override Product CreateProduct()
    {
        return new ProductA();
    }
}

public class ConcreteFactory2 : AbstractFactory
{
    public override Product CreateProduct()
    {
        return new ProductB();
    }
}

Product

public abstract class Product
{
}

public class ProductA : Product
{
}

public class ProductB : Product
{
}

Some Application

public class SomeApplication
{
    private Product product;

    public void Init(AbstractFactory factory)
    {
        this.product = factory.CreateProduct();
    }
}

public class SomeApplicationUser
{
    public void SomeFunction(bool someCondition)
    {
        var someApp = new SomeApplication();

        if (someCondition)
        {
            var factory1 = new ConcreteFactory1();
            someApp.Init(factory1);
        }
        else
        {
            var factory2 = new ConcreteFactory2();
            someApp.Init(factory2);
        }
    }
}

Builder

Result

public class Result
{
    public void AddFoo()
    {
    }
                   
    public void RemoveFoo()
    {
    }
                   
    public void SetBar(int someParam)
    {
    }
}

Builder

public abstract class Builder
{
    public abstract void BuildPartA();
    public abstract void BuildPartB();
    public abstract void BuildPartC();
    public abstract Result GetResult();
}
                                                   
public class ConcreteBuilder : Builder
{
    private Result result = null;
                                                 
    public ConcreteBuilder()
    {
        this.result = new Result();
    }
                                                 
    public override void BuildPartA()
    {
        this.result.AddFoo();
    }
                                                 
    public override void BuildPartB()
    {
        this.result.AddFoo();
        this.result.SetBar(0);
    }
                                                   
    public override void BuildPartC()
    {
        this.result.RemoveFoo();
        this.result.SetBar(1);
    }
                                                   
    public override Result GetResult()
    {
        return result;
    }
}

Director

public class Director
{
    public void Construct(Builder builder)
    {
        builder.BuildPartA();
        builder.BuildPartB();
    }
                                                 
    public void ConstructHoge(Builder builder)
    {
        builder.BuildPartB();
        builder.BuildPartC();
    }
                                                 
    public void ConstructHuga(Builder builder)
    {
        builder.BuildPartA();
        builder.BuildPartB();
        builder.BuildPartC();
    }
}

Some Application

public class SomeApplication
{
    public void SomeFunction(int someCondition)
    {
        var builder = new ConcreteBuilder();
        var director = new Director();
                                                 
        switch (someCondition)
          {
            case 1:
                director.Construct(builder);
                break;
            case 2:
                director.ConstructHoge(builder);
                break;
            case 3:
                director.ConstructHuga(builder);
                break;
        }

        var result = builder.GetResult();
    }
}

Factory Method

・抽象クラスが、具象クラスによって決定されるインスタンスに興味がないとき
・具象クラスにインスタンスを作らせ、抽象クラスではそのインタフェース(抽象クラス)のみを扱う

最もシンプルな実装

Product

public abstract class Product
{
}
                                
public class ProductA : Product
{
}
                                
public class ProductB : Product
{
}

Product User

public abstract class ProductUser
{
    private Product product = null;
                                                                                             
    // factory method
    protected abstract Product CreateProduct();
                                                                                             
    public ProductUser()
    {
        // you should not call CreateProduct() here.
        // because you still cannot use concrete CreateProduct() in super class constructor.
    }
                                                                                             
    public void LazyInit()
    {
        this.product = CreateProduct();
    }
                                                                                             
    public void SomeFunction()
    {
    }
}
                                                                                             
public class ConcreteProductUser1 : ProductUser
{
    protected override Product CreateProduct()
    {
        return new ProductA();
    }
}
                                                                                             
public class ConcreteProductUser2 : ProductUser
{
    protected override Product CreateProduct()
    {
        return new ProductB();
    }
}

Some Application

public class SomeApplication
{
    public void SomeFunction(bool someCondition)
    {
        if (someCondition)
        {
            var user1 = new ConcreteProductUser1();
            user1.LazyInit();
            user1.SomeFunction();
        }
        else
        {
            var user2 = new ConcreteProductUser2();
            user2.LazyInit();
            user2.SomeFunction();
        }
    }
}

テンプレートを使う場合

public class StandartProductUser<T> : ProductUser where T : Product, new()
{
    protected override Product CreateProduct()
    {
        return new T();
    }
}
public class SomeApplication
{
    public void SomeFunction(bool someCondition)
    {
        if (someCondition)
        {
            var user1 = new StandartProductUser<ProductA>();
            user1.LazyInit();
            user1.SomeFunction();
        }
        else
        {
            var user2 = new StandartProductUser<ProductB>();
            user2.LazyInit();
            user2.SomeFunction();
        }
    }
}

Prototype

Product

public class Product
{
    public void Init(int someParam)
    {
    }
                                                       
    public virtual Product Clone()
    {
        return (Product)MemberwiseClone();
    }
}
                                                       
public class ProductB : Product
{
    public override Product Clone()
    {
        return (Product)MemberwiseClone();
    }
}

Prototype Factory

public class PrototypeFactory
{
    private Product prototypeProduct = null;
                                                       
    public PrototypeFactory(Product prototype)
    {
        this.prototypeProduct = prototype;
    }
                                                       
    public Product MakeProduct1()
    {
        var product = this.prototypeProduct.Clone();
        product.Init(1);
        return product;
    }
                                                       
    public Product MakeProduct2()
    {
        var product = this.prototypeProduct.Clone();
        product.Init(2);
        return product;
    }
}

Some Application

public class SomeApplication
{
    public void SomeFunction(bool someCondition)
    {
        Product prototype;
        if (someCondition)
        {
            prototype = new Product();
        }
        else
        {
            prototype = new ProductB();
        }
                                                       
        var factory = new PrototypeFactory(prototype);
                                                       
        var product1 = factory.MakeProduct1();
        var product2 = factory.MakeProduct2();
    }
}

Singleton

public class Singleton
{
    private static Singleton instance = null;
    public static Singleton Instance
    {
        get
        {
            if (instance == null)
                instance = new Singleton();
            return instance;
        }
    }
                                              
    protected Singleton()
    {
    }
}

Adapterパターン

  • 既存クラスを利用したいが、インタフェースが合わない
    Class Adapter
public class Adaptee
{
    public void HugaFunction()
    {
    }
}

public interface Target
{
    public void HogeFunction();
}

public class Adapter : Adaptee, Target
{
    public void HogeFunction()
    {
        this.HogeFunction();
    }
}
Object Adapter
public class Adaptee
{
    public void HugaFunction()
    {
    }
}

public class Target
{
    public virtual void HogeFunction()
    {
    }
}

public class Adapter : Target
{
    private Adaptee adaptee = null;

    public Adapter(Adaptee adaptee)
    {
        this.adaptee = adaptee;
    }

    public override void HogeFunction()
    {
        adaptee.HugaFunction();
    }
}

Bridge

  • ネストされた継承を解消したい
  • 実装の変更に依存させない
  • 実装をサブクラスの追加により拡張可能にすべき

Implementor

public abstract class Implementor
{
    public abstract void SomeFunctionImp();
}

public class ConcreteImplementorA : Implementor
{
    public override void SomeFunctionImp()
    {
    }
}

public class ConcreteImplementorB : Implementor
{
    public override void SomeFunctionImp()
    {
    }
}

Abstraction

public abstract class Abstraction
{
    private Implementor imp = null;

    public Abstraction(Implementor implementor)
    {
        this.imp = implementor;
    }

    public void SomeFunction()
    {
        this.imp.SomeFunctionImp();
    }
}

public class RefinedAbstraction : Abstraction
{
    public RefinedAbstraction(Implementor implementor) : base(implementor)
    {
    }

    public void SomeRefinedFunction()
    {
        SomeFunction();
        SomeFunction();
    }
}

Composite

Component

public abstract class Component
{
    public virtual void Add(Component component)
    {
    }

    public virtual void Remove(Component component)
    {
    }

    public virtual Component GetChild(int index)
    {
        return null;
    }

    public abstract void Operation();
}

Composite, Leaf

using System.Collections.Generic;

public class Composite : Component
{
    private List<Component> children = null;

    public Composite()
    {
        this.children = new List<Component>();
    }

    public override void Add(Component component)
    {
        children.Add(component);
    }

    public override void Remove(Component component)
    {
        children.Remove(component);
    }

    public override Component GetChild(int index)
    {
        if (index < children.Count)
        {
            return children[index];
        }

        return null;
    }

    public override void Operation()
    {
        foreach (var child in children)
        {
            child.Operation();
        }
    }
}

public class Leaf : Component
{
    public override void Operation()
    {
    }
}

Decorator

Component

public abstract class Component
{
    public abstract void Operation();
}

public class ConcreteComponent : Component
{
    public override void Operation()
    {
    }
}

Decorator

public abstract class Decorator : Component
{
    private Component component;

    public Decorator(Component component)
    {
        this.component = component;
    }

    public override void Operation()
    {
        component.Operation();
    }
}

public class ConcreteDecoratorA : Decorator
{
    private int someParam;

    public ConcreteDecoratorA(Component component) : base(component)
    {
    }

    public override void Operation()
    {
        base.Operation();
    }
}

public class ConcreteDecoratorB : Decorator
{
    public ConcreteDecoratorB(Component component) : base(component)
    {
    }

    public override void Operation()
    {
        base.Operation();
        SomeFunction();
    }

    public void SomeFunction()
    {
    }
}

SomeApplication

public class SomeApplication
{
    public void SomeFunction()
    {
        Component comp = new ConcreteComponent();
        Component deco1 = new ConcreteDecoratorA(comp);
        Component deco2 = new ConcreteDecoratorB(comp);
        Component deco3 = new ConcreteDecoratorA(new ConcreteDecoratorB(comp));
        Component deco4 = new ConcreteDecoratorA(new ConcreteDecoratorB(new ConcreteDecoratorB(comp)));
    }
}

Facade

・サブシステムの構成要素を隠ぺい及び結合を弱める

Facade

using SomeSubSystems;

public class Facade
{
    public void SomeFunction()
    {
        var subSystemA = new SubSystemA();
        var subSystemB = new SubSystemB();
        var subSystemC = new SubSystemC();

        subSystemA.FunctionA();
        subSystemB.FunctionB();
        subSystemC.FunctionC();
    }
}
namespace SomeSubSystems
{
    public class SubSystemA
    {
        public void FunctionA()
        {
        }
    }

    public class SubSystemB
    {
        public void FunctionB()
        {
        }
    }

    public class SubSystemC
    {
        public void FunctionC()
        {
        }
    }
}

Flyweight

Flyweight

public abstract class Flyweight
{
    public abstract void Operation(ExtrinsicState extrinsicState);
}

public class ConcreteFlyweight : Flyweight
{
    private int someIntrinsicState;

    public override void Operation(ExtrinsicState extrinsicState)
    {
    }
}

public class UnsharedConcreteFlyweight : Flyweight
{
    private int allState;

    public override void Operation(ExtrinsicState extrinsicState)
    {
    }
}

ExtrinsicState

public class ExtrinsicState
{
    public int someParam;
}

FlyweightFactory

using System.Collections.Generic;

public class FlyweightFactory
{
    private Dictionary<string, ConcreteFlyweight> flyweights;

    public FlyweightFactory()
    {
        flyweights = new Dictionary<string, ConcreteFlyweight>();
    }

    public ConcreteFlyweight CreateConcreteFlyweight(string key)
    {
        if (!flyweights.ContainsKey(key))
        {
            flyweights.Add(key, new ConcreteFlyweight());
        }

        return flyweights[key];
    }

    public UnsharedConcreteFlyweight CreateUnsharedConcreteFlyweight()
    {
        return new UnsharedConcreteFlyweight();
    }
}

Some Application

public class SomeApplication
{
    public void SomeFuncition()
    {
        var factory = new FlyweightFactory();
        var exState = new ExtrinsicState();

        for (int i = 0; i < 100; i++)
        {
            // create 100 foo
            var concreteFlyweight = factory.CreateConcreteFlyweight("foo");
            exState.someParam = i;
            concreteFlyweight.Operation(exState);
        }

        for (int i = 0; i < 30; i++)
        {
            // create 30 bar
            var concreteFlyweight = factory.CreateConcreteFlyweight("bar");
            exState.someParam = i;
            concreteFlyweight.Operation(exState);
        }

        var unsharedFlyweight = factory.CreateUnsharedConcreteFlyweight();
        unsharedFlyweight.Operation(exState);
    }
}

Proxy

Subject

public abstract class Subject
{
    public abstract void Request();
}

public class RealSubject : Subject
{
    public override void Request()
    {
    }
}
public class Proxy : Subject
{
    RealSubject realSubject = null;

    public override void Request()
    {
        // you can manage how to access here
        realSubject.Request();
    }
}

Chain of Responsibility

・受け手を明確にせずに、複数あるオブジェクトの1つに対して要求を発行したい

Handler

public abstract class Handler
{
    protected Handler successor;

    public virtual void HandleRequest()
    {
        if (successor != null)
        {
            successor.HandleRequest();
        }
    }
}

public class ConcreteHandler1 : Handler
{
    public override void HandleRequest()
    {
        base.HandleRequest();
    }
}

public class ConcreteHandler2 : Handler
{
    public override void HandleRequest()
    {
        System.Console.WriteLine("some customized handle");
    }
}

SomeApplication

public class SomeApplication
{
    public void SomeFunction()
    {
        var handler1 = new ConcreteHandler1();
        var handler2 = new ConcreteHandler2();
        handler1.HandleRequest();
    }
}

Command

Command

public abstract class Command
{
    public abstract void Execute();
}

public class ConcreteCommand : Command
{
    private int someState;
    Receiver receiver = null;

    public ConcreteCommand(Receiver receiver)
    {
        this.receiver = receiver;
    }

    public override void Execute()
    {
        receiver.SomeAction();
    }
}

Receiver

public class Receiver
{
    public void SomeAction()
    {
    }
}
public class SomeApplication
{
    private List<Command> commandList = null;
    private int currentIndex = 0;

    public void SomeFunction()
    {
        this.commandList = new List<Command>();

        var receiver1 = new Receiver();
        var command1 = new ConcreteCommand(receiver1);
        commandList.Add(command1);

        var receiver2 = new Receiver();
        var command2 = new ConcreteCommand(receiver2);
        commandList.Add(command2);
    }

    public void ExecuteNext()
    {
        commandList[currentIndex].Execute();
        currentIndex++;
    }
}

Interpreter

  • 単純な文法の言語を解釈する

Interpreter

using System;

public abstract class AbstractExpression
{
    public abstract void Interpret(Context context);
}

public class TerminalExpression : AbstractExpression
{
    private string literal;

    public TerminalExpression(string literal)
    {
        this.literal = literal;
    }

    public override void Interpret(Context context)
    {
        // some interpret
        Console.Write(this.literal);
    }
}

public class NonterminalExpression : AbstractExpression
{
    private TerminalExpression exp1;
    private TerminalExpression exp2;

    public NonterminalExpression(TerminalExpression exp1, TerminalExpression exp2)
    {
        this.exp1 = exp1;
        this.exp2 = exp2;
    }

    public override void Interpret(Context context)
    {
        // some interpret
        Console.Write(this.exp1 + "&" + this.exp2);
    }
}

Context

public class Context
{
    public string SomeInfo;
}

Client

public class Client
{
    public void SomeFunction()
    {
        var terminalExp1 = new TerminalExpression("literal1");
        var terminalExp2 = new TerminalExpression("literal2");
        var someExp = new NonterminalExpression(terminalExp1, terminalExp2);
        var context = new Context { SomeInfo = "someINfo" };
        someExp.Interpret(context);
    }
}

Iterator

Aggregate

public abstract class Aggregate
{
    public abstract Iterator CreateIterator();
}

public class ConcreteAggregate : Aggregate
{
    public override Iterator CreateIterator()
    {
        return new ConcreteIterator(this);
    }
}

Iterator

public abstract class Iterator
{
    protected Aggregate aggregate;
    public Iterator(Aggregate aggregate)
    {
        this.aggregate = aggregate;
    }

    public abstract void First();
    public abstract void Next();
    public abstract bool IsDone();
    public abstract SomeItem CurrentItem();
}

public class ConcreteIterator : Iterator
{
    private int currentIndex = 0;

    public ConcreteIterator(Aggregate aggregate) : base(aggregate)
    {
    }

    public override void First()
    {
        // some algorithm
        this.currentIndex = 0;
    }

    public override void Next()
    {
        // some algorithm
        this.currentIndex++;
    }

    public override bool IsDone()
    {
        // some algorithm
        return this.currentIndex < aggregate.Count;
    }

    public override SomeItem CurrentItem()
    {
        // some algorithm
        return this.currentIndex[currentIndex];
    }
}

SomeApplication

public class SomeItem
{
}

public class SomeApplication
{
    private Aggregate someAggregate = null;

    public void SomeFunction()
    {
        var iterator = this.someAggregate.CreateIterator();
        iterator.First();
        while (!iterator.IsDone())
        {
            iterator.Next();
            var item = iterator.CurrentItem();
        }
    }
}

Mediator

Mediator

public abstract class Mediator
{
    public abstract void CreateColleagues();
    public abstract void ColleagueChanged(Colleague colleague);
}

public class ConcreteMediator : Mediator
{
    private ConcreteColleague1 colleague1;
    private ConcreteColleague2 colleague2;

    public override void CreateColleagues()
    {
        this.colleague1 = new ConcreteColleague1(this);
        this.colleague2 = new ConcreteColleague2(this);
    }

    public override void ColleagueChanged(Colleague colleague)
    {
        if (colleague == this.colleague1)
        {
            this.colleague1.SetFoo();
        }
        else if (colleague == this.colleague2)
        {
            this.colleague2.SetBar();
        }
    }
}

Colleague

public abstract class Colleague
{
    protected Mediator mediator;

    public Colleague(Mediator mediator)
    {
        this.mediator = mediator;
    }

    protected void Changed()
    {
        this.mediator.ColleagueChanged(this);
    }
}

public class ConcreteColleague1 : Colleague
{
    public ConcreteColleague1(Mediator mediator) : base(mediator)
    {
    }

    public void HogeEvent()
    {
        // some event such as mouse click
        Changed();
    }

    public void SetFoo()
    {
        // some action
    }
}

public class ConcreteColleague2 : Colleague
{
    public ConcreteColleague2(Mediator mediator) : base(mediator)
    {
    }

    public void HugaEvent()
    {
        // some event such as mouse click
        Changed();
    }

    public void SetBar()
    {
        // some action
    }
}

Some Application

public class SomeApplication
{
    public void SomeFunction()
    {
        var mediator = new ConcreteMediator();
        mediator.CreateColleagues();
    }
}

Memento

  • カプセル化を破壊せずに、ある時点のスナップショットを残しておき、後に復元等で利用する

Memento

public interface IMementoNarrow
{
}

public interface IMementoWide
{
    public void SetState(State state);
    public State GetState();
}

public class Memento : IMementoNarrow, IMementoWide
{
    private State state;

    public Memento()
    {

    }

    public void SetState(State state)
    {
        this.state = state;
    }

    public State GetState()
    {
        return this.state;
    }
}

public class State
{
    // some state params
    public string someState;
}

Originator

public class Originator
{
    public IMementoNarrow CreateMemento()
    {
        return new Memento();
    }

    public void SetMemento(IMementoNarrow memento)
    {
        var mementoWide = memento as IMementoWide;
    }

    public void Foo()
    {

    }
}

Caretaker

public class Caretaker
{
    private Originator originator;
    private IMementoNarrow prevMemento;

    public void SomeExecuteFunction()
    {
        // execute something
        this.originator.Foo();

        this.prevMemento = this.originator.CreateMemento();
    }

    public void SomeUndoFunction()
    {
        this.originator.SetMemento(this.prevMemento);
    }
}

Observer

Subject

public abstract class Subject
{
    protected List<Observer> observers = null;

    public virtual void Attach(Observer observer)
    {
        this.observers.Add(observer);
    }

    public virtual void Detach(Observer observer)
    {
        this.observers.Remove(observer);
    }

    public virtual void Notify()
    {
        foreach (var observer in this.observers)
        {
            observer.Update(this);
        }
    }
}

public class ConcreteSubject : Subject
{
    public void Foo()
    {
        // do something

        Notify();
    }
}

Observer

public abstract class Observer
{
    public abstract void Update(Subject changedSubject);
}

public class ConcreteObserver : Observer
{
    public override void Update(Subject changedSubject)
    {
        // do something update
    }
}

SomeApplication

public class SomeApplication
{
    public void SomeFunction()
    {
        var subject = new ConcreteSubject();
        subject.Foo();
    }
}

State

Context

public class Context
{
    private State currentState;
    public ConcreteStateA stateA { get; private set; }
    public ConcreteStateB stateB { get; private set; }

    public Context()
    {
        this.stateA = new ConcreteStateA();
        this.stateB = new ConcreteStateB();
        this.currentState = stateA;
    }

    public void Request()
    {
        this.currentState.Handle(this);
    }

    public void ChangeState(State state)
    {
        this.currentState = state;
    }
}

State

public abstract class State
{
    public abstract void Handle(Context context);
    public virtual void ChangeState(Context context, State state)
    {
        context.ChangeState(state);
    }
}

public class ConcreteStateA : State
{
    public override void Handle(Context context)
    {
        ChangeState(context, context.stateB);
    }
}

public class ConcreteStateB : State
{
    public override void Handle(Context context)
    {
        ChangeState(context, context.stateA);
    }
}

SomeApplication

public class SomeApplication
{
    public void SomeFunction()
    {
        var context = new Context();
        context.Request();
    }
}

Strategy

Context

public class Context
{
    private Strategy strategy;

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

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

Strategy

public abstract class Strategy
{
    public abstract void AlgorithmInterface();
}

public class ConcreteStrategyA : Strategy
{
    public override void AlgorithmInterface()
    {
        // strategy A algorithm
    }
}

public class ConcreteStrategyB : Strategy
{
    public override void AlgorithmInterface()
    {
        // strategy B algorithm
    }
}

public class ConcreteStrategyC : Strategy
{
    public override void AlgorithmInterface()
    {
        // strategy C algorithm
    }
}

SomeApplication

public class SomeApplication
{
    public void SomeFunction()
    {
        // when use strategy A
        {
            var strategyA = new ConcreteStrategyA();
            var context = new Context(strategyA);
            context.ContextInterface();
        }

        // when use strategy B 
        {
            var strategyB = new ConcreteStrategyA();
            var context = new Context(strategyB);
            context.ContextInterface();
        }

        // when use strategy C
        {
            var strategyC = new ConcreteStrategyA();
            var context = new Context(strategyC);
            context.ContextInterface();
        }
    }
}

Template Method

  • ハリウッドの原則

TemplateMethod

public abstract class AbstractClass
{
    public void TemplateMethod()
    {
        PrimitiveOperation1();
        PrimitiveOperation2();
    }

    protected abstract void PrimitiveOperation1();
    protected abstract void PrimitiveOperation2();
}

public class ConcreteClass : AbstractClass
{
    protected override void PrimitiveOperation1()
    {
        // some first operation
    }

    protected override void PrimitiveOperation2()
    {
        // some second operation
    }
}

Visitor

  • オペレーションをカプセル化する
  • 多対多の関係で柔軟

Visitor

public abstract class Visitor
{
    public abstract void VisitConcreteElementA(ConcreteElementA elementA);
    public abstract void VisitConcreteElementB(ConcreteElementB elementB);
}

public class ConcreteVisitor1 : Visitor
{
    public override void VisitConcreteElementA(ConcreteElementA elementA)
    {
        // some action
    }

    public override void VisitConcreteElementB(ConcreteElementB elementB)
    {
        // some action
    }
}

public class ConcreteVisitor2 : Visitor
{
    public override void VisitConcreteElementA(ConcreteElementA elementA)
    {
        // some action
    }

    public override void VisitConcreteElementB(ConcreteElementB elementB)
    {
        // some action
    }
}

Element

public abstract class Element
{
    public abstract void Accept(Visitor visitor);
}

public class ConcreteElementA : Element
{
    public override void Accept(Visitor visitor)
    {
        visitor.VisitConcreteElementA(this);
    }
}

public class ConcreteElementB : Element
{
    public override void Accept(Visitor visitor)
    {
        visitor.VisitConcreteElementB(this);
    }
}

Structure

public class ObjectStructure
{
    public IEnumerable<Element> SomeStructure;
}

SomeApplication

public class SomeApplication
{
    public void SomeFunction()
    {
        var structureA = new ObjectStructure
        {
            SomeStructure = new List<ConcreteElementA>().Cast<Element>()
        };

        var structureB = new ObjectStructure
        {
            SomeStructure = new List<ConcreteElementB>().Cast<Element>()
        };

        var visitor1 = new ConcreteVisitor1();
        var visitor2 = new ConcreteVisitor2();

        // when use structureA and visitor1
        foreach (var element in structureA.SomeStructure)
        {
            element.Accept(visitor1);
        }

        // when use structureA and visitor2
        foreach (var element in structureA.SomeStructure)
        {
            element.Accept(visitor2);
        }

        // when use structureB and visitor1
        foreach (var element in structureB.SomeStructure)
        {
            element.Accept(visitor1);
        }

        // when use structureB and visitor2
        foreach (var element in structureB.SomeStructure)
        {
            element.Accept(visitor2);
        }
    }
}