65.9K
CodeProject 正在变化。 阅读更多。
Home

理解和实现 C# 中的桥模式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (20投票s)

2012 年 8 月 4 日

CPOL

3分钟阅读

viewsIcon

82968

downloadIcon

708

本文讨论桥接模式,并尝试使用一个简单的应用程序来演示它。

引言

让我们在本文中讨论“桥接模式”。当我们想到桥接模式时,首先想到的是“它是在两个不同的实现之间的一种桥梁”。但事实并非如此。上面提到的这是适配器模式的任务,这里讨论了适配器模式。但要谈论桥接模式,该模式专门设计用于使抽象和实现独立变化。

背景

GoF 将桥接模式定义为“将抽象与其实现解耦,以便两者可以独立变化。”

此模式可以使用的最佳示例是插件或驱动程序的使用。如果我们有一个应用程序可以使用任何特定的驱动程序,那么我们可以使用桥接模式。一个经典的例子是像“半条命”这样的游戏中视频配置选择屏幕的场景。该屏幕为用户提供了以OpenGLDirect3D软件模式运行游戏的选项。当用户进行选择时,我们可以使用特定于选择的具体的实现,而不会对抽象产生任何影响。

现在让我们可视化桥接模式。 

现在让我们看看此类图中每个组件代表什么。

  • Implementor:此接口提供了所有插件应该提供的初步操作。
  • ConcreteImplementor:这些是实现Implementor接口的实际类。这些类将被有选择地使用,比如插件。
  • Abstraction:此类为客户端提供了一个抽象。客户端将继续使用此抽象,甚至无需关心内部选择了哪个实际插件,即ConcreteImplementor
  • RefinedAbstraction:此类将实现 Abstraction 类的实际功能。选择适当的ConcreteImplementor是此类的责任。

使用代码 

现在让我们尝试使用桥接模式实现一个小例子。让我们想象一个虚构的智能电视,它为用户提供了播放本地有线电视、本地卫星电视或 IPTV 源的可能性。在这里,用户可以从选定的源请求电视指南。他还可以选择播放选定的电视频道。 

因此,从电视的角度来看,获取指南和播放电视的抽象。每个源的实际实现将有所不同。因此,让我们继续,首先定义每个电视源将提供的抽象,即来自上述图表的Implementor

interface IVideoSource
{
	string GetTvGuide();
	string PlayVideo();
}

现在让我们为每个电视源定义ConcreteImplementors

class LocalCabelTv : IVideoSource
{
	const string SOURCE_NAME = "Local Cabel TV";

	string IVideoSource.GetTvGuide()
	{
		return string.Format("Getting TV guide from - {0}", SOURCE_NAME);
	}

	string IVideoSource.PlayVideo()
	{
		return string.Format("Playing - {0}", SOURCE_NAME);
	}
}

class LocalDishTv : IVideoSource
{
	const string SOURCE_NAME = "Local DISH TV";

	string IVideoSource.GetTvGuide()
	{
		return string.Format("Getting TV guide from - {0}", SOURCE_NAME);
	}

	string IVideoSource.PlayVideo()
	{
		return string.Format("Playing - {0}", SOURCE_NAME);
	}
}

class IPTvService : IVideoSource
{
	const string SOURCE_NAME = "IP TV";

	string IVideoSource.GetTvGuide()
	{
		return string.Format("Getting TV guide from - {0}", SOURCE_NAME);
	}

	string IVideoSource.PlayVideo()
	{
		return string.Format("Playing - {0}", SOURCE_NAME);
	}
}

因此,我们可以看到每个ConcreteImplementor提供相同的抽象,但实际实现有所不同(目前我们有虚拟消息来显示不同的实现)。

现在让我们实现RefinedAbstraction,即实际的电视类,它将使用这些ConcreteImplementors来执行播放。(为了简单起见,我将AbstractionRefinedAbstraction合并在单个类中,没有任何接口)

class MySuperSmartTV
{
	IVideoSource currentVideoSource = null;

	public IVideoSource VideoSource
	{
		get
		{
			return currentVideoSource;
		}
		set
		{
			currentVideoSource = value;
		}
	}

	public void ShowTvGuide()
	{
		if (currentVideoSource != null)
		{
			Console.WriteLine(currentVideoSource.GetTvGuide());
		}
		else
		{
			Console.WriteLine("Please select a Video Source to get TV guide from");
		}
	}

	public void PlayTV()
	{
		if (currentVideoSource != null)
		{
			Console.WriteLine(currentVideoSource.PlayVideo());
		}
		else
		{
			Console.WriteLine("Please select a Video Source to play");
		}
	}
}

下一步是根据用户的选择使用适当的实现。因此,让我们让用户选择适当的电视源,然后从该源获取电视指南,甚至开始播放该源。

class SuperSmartTvController
{
	static void Main(string[] args)
	{
		MySuperSmartTV myTv = new MySuperSmartTV();
					
		Console.WriteLine("Select A source to get TV Guide and Play");
		Console.WriteLine("1. Local Cable TV\n2. Local Dish TV\n3. IP TV");

		ConsoleKeyInfo input = Console.ReadKey();

		// Let us see what user has selected and select the video source accrodingly
		switch (input.KeyChar)
		{
			case '1':
				myTv.VideoSource = new LocalCabelTv();
				break;

			case '2':
				myTv.VideoSource = new LocalDishTv();
				break;

			case '3':
				myTv.VideoSource = new IPTvService();
				break;                
		}

		Console.WriteLine(); //some whitespace on output for readability

		//Let us show the TV guide from selected source
		myTv.ShowTvGuide();

		//Let us now play the selected TV source.
		myTv.PlayTV();

		Console.WriteLine(); //some whitespace on output for readability
	}
}

现在用户可以选择电视源,每个电视源将有自己的实现,但可以与相同的抽象一起使用,就像基于插件的架构一样。电视源的接口现在完全独立于每个电视源的实现。

让我们运行应用程序并选择 IPTV 作为源

在结束我们的讨论之前,让我们看一下我们应用程序的类图,并将其与上面显示的桥接模式的类图进行映射。

关注点 

由于接口比实现更稳定(即更改的可能性较小),因此将类的接口与实现分开似乎是一个好主意。

历史

  • 2012年8月4日:第一个版本
© . All rights reserved.