回顾游戏中的设计模式-策略模式vs抽象工厂

发表于2018-10-15
评论5 5.3k浏览
前言:
最近有时间看看设计模式方面的书,好多东西时间久了不看就变得生疏起来, 在这里会抽出时间把自己在工作中使用过的设计模式做下总结。

刚才在看到别人说,简单工厂模式【通过提供单独的一个类,来实现创建实例对象的过程】,可以使用反射来替换掉程序中的switch/if..else,嗯,这是没问题的,但实际应用中,在移动端还要尽量少的去频繁使用反射Reflection,严重依赖字符串的功能都会存在效率的问题。

今天在看到策略模式(Strategy Pattern)的时候,突然间意识到自己在16年的一个项目中,有一处应用不够合理。

当时在存储游戏数据部分,我是通过直接将对象图转化成字节流的形式,即序列化。
那么序列化有多种方式,你可以序列化成XML,也可以是二进制,或者是SOAP(类似于XML,不建议使用,只是为了多提供一个策略演示)

那么这时候,采用哪种设计模式? 
当时使用的是抽象工厂,但实际上,他更符合策略模式。即我们将多个“策略”抽象成接口的形式来解耦合。

比如说我要回大连,我可以坐火车,飞机,客车,或是自驾。我最终的目的是相同的,只是到达目的的方式不同。

然而在代码上,策略模式和抽象工厂区别不是很大,后来我搜索了一下关于两个设计模式之间的区别,找到了一个非常好的解释 。

策略模式vsr抽象工厂的区别

https://bbs.csdn.net/topics/320073328

2009年的一个帖子。

解释如下:

抽象工厂属于创建型的,而策略模式属于行为型。

抽象工厂往往是抽取的同一基类(Class)的不同子类。

策略模式往往抽取的是同一接口的不同实现。

那么显然,我在序列化的时候,我序列化为哪种格式,只是行为不同(纯行为上),结果相同,而且也符合同一接口的不同实现。

那么说到这里了,就把之间的代码给修改一下,修改为策略模式。

定义序列化接口:
ISerializable.cs
public interface ISerializable {
	void Serialize<T> (string filePath, T data);
	T Deserialize<T> (string filePath);
}

声明了两个方法,序列化和反序列化。

下面实现具体的序列化类,分别是Binary,XML,SOAP

BinarySerialized.cs

using System.IO;
using System;
using System.Xml;
using System.Runtime.Serialization.Formatters.Binary;
public class BinarySerialized : ISerializable {
	public void Serialize<T> (string filePath, T data)
	{
		using (FileStream fs = new FileStream(filePath, FileMode.Create))
        {
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(fs, data);
        }
	}
	public T Deserialize<T> (string filePath)
	{
		if (!File.Exists(filePath))
        {
            return default(T);
        }
       // return base.Deserialize<T>();
        using (FileStream fs = new FileStream(filePath, FileMode.Open))
        {
            BinaryFormatter bf = new BinaryFormatter();
            T data = (T)bf.Deserialize(fs);
            if (data != null)
            {
                return data;
            }
        }
        return default(T);
	}
}

XMLSerialized .cs

using System.IO;
using System.Xml;
using System.Xml.Serialization;
public class XMLSerialized : ISerializable {
	public void Serialize<T> (string filePath, T data)
	{
		using (XmlTextWriter xWrite = new XmlTextWriter(filePath, null))
		{
			XmlSerializer sl = new XmlSerializer(data.GetType());
			sl.Serialize(xWrite, data);
		}
	}
	public T Deserialize<T> (string filePath)
	{
		if (!File.Exists(filePath))
		{
			return default(T);
		}
		using (XmlReader xRead = new XmlTextReader(filePath))
		{
			XmlSerializer sl = new XmlSerializer(typeof(T));
			T data = (T)sl.Deserialize(xRead);
			if (data != null)
			{
				return data;
			}
		}
		return default(T);
	}
}


SOAPSerialized.cs(SOAP要引入dll[System.Runtime.Serialization.Formatters.Soap.dll])

using System.IO;
using System.Runtime.Serialization.Formatters.Soap;
public class SOAPSerialized : ISerializable {
	public void Serialize<T> (string filePath, T data)
	{
		using (FileStream fs = new FileStream(filePath, FileMode.Create))
		{
			SoapFormatter bf = new SoapFormatter();
			bf.Serialize(fs, data);
		}
	}
	public T Deserialize<T> (string filePath)
	{
		if (!File.Exists(filePath))
		{
			return default(T);
		}
		using (FileStream fs = new FileStream(filePath, FileMode.Open))
		{
			SoapFormatter bf = new SoapFormatter();
			T data = (T)bf.Deserialize(fs);
			if (data != null)
			{
				return data;
			}
		}
		return default(T);
	}
} 
最后我们定义一个序列化的上下文,面向接口编程:
SerializableContext.cs

public class SerializableContext{
	private ISerializable SerializableStrategy;
	public SerializableContext(ISerializable strategy)
	{
		this.SerializableStrategy = strategy;
	}
	public void ExecuteSerialize<T> (string filePath, T data)
	{
		SerializableStrategy.Serialize<T> (filePath, data);
	}
	public T ExecuteDeserialize<T> (string filePath){
		return SerializableStrategy.Deserialize<T> (filePath);
	}
}

演示代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SerializedDemo: MonoBehaviour {
	public SerializableContext context;
	private string FILE_PATH;
	DataManager data;
	// Use this for initialization
	void Start () {
		FILE_PATH = Application.persistentDataPath + "/test20181015";
		data = new DataManager();
	}
	// Update is called once per frame
	void Update () {
	}
	public void OnGUI()
	{
		if (GUILayout.Button ("Serialized XML")) {
			context =new SerializableContext (new XMLSerialized());
			context.ExecuteSerialize (FILE_PATH, data);
		}
		if (GUILayout.Button ("Serialized Binary")) {
			context =new SerializableContext (new BinarySerialized());
			context.ExecuteSerialize (FILE_PATH, data);
		}
		if (GUILayout.Button ("Serialized SOAP")) {
			context =new SerializableContext (new SOAPSerialized());
			context.ExecuteSerialize (FILE_PATH, data);
		}
		if (GUILayout.Button ("Deserialized")) {
			DataManager d = context.ExecuteDeserialize<DataManager>(FILE_PATH);
			if (d != null)
			{
				d.debug();
			}
		}
	}
}
[System.Serializable]
public class DataManager
{
    List<Person> personList;
    List<PetData> petList;
    public DataManager()
    {
        personList = new List<Person>();
        Person person1 = new Person();
        person1.id = 1;
        person1.name = "one";
        Person person2 = new Person();
        person2.id = 2;
        person2.name = "two";
        personList.Add(person1);
        personList.Add(person2);
        petList = new List<PetData>();
        PetData pet1 = new PetData();
        pet1.id = 10;
        pet1.name = "john";
        pet1.petQuality = 1;
        PetData pet2= new PetData();
        pet2.id = 20;
        pet2.name = "lucy";
        pet2.petQuality = 2;
        PetData pet3 = new PetData();
        pet3.id = 30;
        pet3.name = "david";
        pet3.petQuality = 3;
        petList.Add(pet1);
        petList.Add(pet2);
        petList.Add(pet3);
    }
    public void debug()
    {
        foreach (Person p in personList)
        {
            Debug.Log(p.ToString());
       }
        foreach (PetData p in petList)
        {
            Debug.Log(p.ToString());
        }
    }
}
[System.Serializable]
class Person
{
    public int id;
    public string name;
    public string ToString()
    {
        return "Person:id:" + id.ToString() + ",name:" + name;
    }
}
[System.Serializable]
class PetData
{
    public int id;
    public string name;
    public int petQuality;
    public string ToString()
    {
        return "PetData:id:" + id.ToString() + ",name:" + name+",quality:"+petQuality.ToString();
    }
}

在这里定义了一个DataManager类,DataManager包含了

 List<Person> personList;
 List<PetData> petList;

两个字段,并进行初始化,然后对DataManager类,进行序列化和反序列化测试。



演示代码下载(微云):

链接:https://share.weiyun.com/5Cg9MT4

感谢您的阅读,如文中有误,欢迎指正~




如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引