小话设计模式(三)抽象工厂模式

本文介绍了一种解决跨移动平台开发中API差异性的方法——抽象工厂模式。通过定义一系列接口并利用工厂模式创建这些接口的不同实现,可以有效地隐藏平台间的差异。文章还探讨了如何通过反射进一步简化工厂模式。

跨移动平台开发App的时候,要根据IOS和Android(或者其他平台)的各自的API来创建统一的接口,因为平台的差异性,往往需要大量的代码来统一。我们常用的跨平台的(游戏)开发工具,例如Cocos2dx和Unity3d,都帮我们实现了部分常用的接口,让我们大部分时候可以忽略平台的差异性。本文并不是要探究他们的实现方法,而是为这种平台差异带来的问题提供一种可行的解决方案——抽象工厂(Abstract Factory)模式。

抽象工厂(又名Kit)提供一种创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类。

假设我们需要将IOS和Android的触摸、键盘和加速仪三个功能各自封装成接口。例如:

public interface ITouch
{
	float x { get;}
	float y { get;}
}

public interface IKeyboard
{
	char key { get;}
	void Show ();
	void Hide ();
}

public interface IAccelerometer
{
	float x { get;}
	float y { get;}
	float z { get;}
}

那么两个平台就需要分别实现这些接口类型。

public class IOSTouch : ITouch
{
	public float x
	{
		get{
			//TODO
			return 0;
		}
	}
	public float y
	{
		get{
			//TODO
			return 0;
		}
	}
}

public class IOSKeyboard : IKeyboard
{
	public char key 
	{ 
		get{ 
			//TODO
			return 'I';
		}
	}
	public void Show ()
	{
		//TODO
	}
	public void Hide ()
	{
		//TODO
	}
}

public class IOSAccelerometer : IAccelerometer
{
	public float x 
	{ 
		get{ 
			//TODO
			return 0;
		}
	}
	public float y
	{ 
		get{ 
			//TODO
			return 0;
		}
	}
	public float z
	{ 
		get{ 
			//TODO
			return 0;
		}
	}
}

public class AndroidTouch : ITouch
{
	public float x
	{
		get{
			//TODO
			return 0;
		}
	}
	public float y
	{
		get{
			//TODO
			return 0;
		}
	}
}

public class AndroidKeyboard : IKeyboard
{
	public char key 
	{ 
		get{ 
			//TODO
			return 'N';
		}
	}
	public void Show ()
	{
		//TODO
	}
	public void Hide ()
	{
		//TODO
	}
}

public class AndroidAccelerometer : IAccelerometer
{
	public float x 
	{ 
		get{ 
			//TODO
			return 0;
		}
	}
	public float y
	{ 
		get{ 
			//TODO
			return 0;
		}
	}
	public float z
	{ 
		get{ 
			//TODO
			return 0;
		}
	}
}

接着,我们新建一个工厂接口类型,用于创建这些类:

public interface IFactory
{
	ITouch CreateTouch();
	IKeyboard CreateKeyboard();
	IAccelerometer CreateAccelerometer();
}

分别实现IOS和Android的工厂类:

public class IOSFactory : IFactory
{
	public ITouch CreateTouch()
	{
		return new IOSTouch();
	}
	public IKeyboard CreateKeyboard()
	{
		return new IOSKeyboard();
	}
	public IAccelerometer CreateAccelerometer()
	{
		return new IOSAccelerometer();
	}
}

public class AndroidFactory : IFactory
{
	public ITouch CreateTouch()
	{
		return new AndroidTouch();
	}
	public IKeyboard CreateKeyboard()
	{
		return new AndroidKeyboard();
	}
	public IAccelerometer CreateAccelerometer()
	{
		return new AndroidAccelerometer();
	}
}

当我们使用的时候,我们首先要实现一个获取工厂的方法:

	static readonly public string PLATFORM = "IOS";

	IFactory GetFactory()
	{
		IFactory ret = null;
		switch (PLATFORM) {
		case "IOS":
			ret = new IOSFactory();
			break;
		case "Android":
			ret = new AndroidFactory ();
			break;
		}
		return ret;
	}
然后调用:

		IFactory factory = GetFactory();
		IKeyboard keyboard = factory.CreateKeyboard ();
		keyboard.Show ();

由此,我们可以看出来,抽象工厂模式非常的重量级。有没有什么方法可以改善?

我们可以考虑使用反射来简化。现在,就是现在,抛弃之前的所有工厂,新建一个类:

public class RFactory
{
	#if PLATFORM_ANDROID
	static readonly public string PLATFORM = "Android";
	#else
	static readonly public string PLATFORM = "IOS";
	#endif

	private static object Create(string className)
	{
		string name = PLATFORM + className;
		Type type = Type.GetType (name);
		return Activator.CreateInstance (type);
	}

	public static ITouch CreateTouch()
	{
		return (ITouch)Create ("Touch");
	}
	public static IKeyboard CreateKeyboard()
	{
		return (IKeyboard)Create ("Keyboard");
	}
	public static IAccelerometer CreateAccelerometer()
	{
		return (IAccelerometer)Create ("Accelerometer");
	}
}

然后,我们就可以这样使用:

		IKeyboard kb = RFactory.CreateKeyboard ();
		kb.Hide ();
当然,我们的开发环境一般是windows或者mac,所以PLATFORM需要根据不同的宏来赋值,windows或者mac下也应该有对应的触摸(鼠标)、键盘和加速计(假)的实现。而这些字符串都可以通过配置文件来配置,包括不同实现的命名空间(你可能注意到了,我们根据字符串创建类的时候,没有考虑命名空间),只需要根据不同的平台读取不同的配置文件即可。


最后再说两句:

其实抽象工厂和工厂方法…………………………………………没有区别还是有一些区别的。

我可以将IFactory分别拆成ITouchFactory、IKeyboardFactory和IAccelerometerFactory,并将IOS和Android的工厂也相应的拆掉,这样就变成了工厂方法。然而这样并没有什么意义,因为你不可能同时使用Android的触摸和IOS的键盘。

我们也可以把多个工厂方法合并,例如物品工厂和角色工厂合并在一起,这样就变成了抽象工厂。然而这同样也没有什么意义,也是错误的,因为你没有办法去限定一个情况下只创建道具实例和玩家实例,而另一种情况下只创建武器实例和NPC实例,而且违反了单一责任原则和接口隔离原则。

由此我们可以得出二者的区别:

抽象工厂创建的是多个相关关联的类,而工厂方法创建的是一个单独的类。

抽象工厂往往受限于环境,每个环境只会有一个具体的工厂类型发挥作用,而工厂方法一般不受限于环境,每个工厂类型都有可能发挥作用。

抽象工厂一般用于跨平台或者跨工具(例如不同的DB,不同的UI框架),而工厂方法往往用于程序具体逻辑。


关于工厂方法模式,参考小话设计模式(二)工厂方法模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值