39

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

 5 years ago
source link: http://gad.qq.com/article/detail/287679
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
前言:
最近有时间看看设计模式方面的书,好多东西时间久了不看就变得生疏起来, 在这里会抽出时间把自己在工作中使用过的设计模式做下总结。

刚才在看到别人说,简单工厂模式【通过提供单独的一个类,来实现创建实例对象的过程】,可以使用反射来替换掉程序中的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类,进行序列化和反序列化测试。


5bc438471c39a.png

演示代码下载(微云):

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

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





About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK