Inheritance, Interfaces, and code duplication

Started by
12 comments, last by NightCreature83 11 years ago

If I understand the code correctly, I am seeing 2 problems here. The Encounter class does too many things and Mission is only vaguely defined.

Encounter currently handles Mission creation and Mission enumeration.

You are saying, there will be different kind of Missions with different data. Would it be possible to make Mission a base class and extend from there different Mission types? Maybe it would even be possible to model all the different scenarious by making Mission a container for "MissionObjectives".

Then you can move the creation logic out of the Encounter class and create a list of Mission objects which are filled by factories based on the xml supplied.

Advertisement
Its actually a product of some bad naming choices. Mission is only vaguly defined because its a concrete data class. The MissionEncounter class is what loads all the mission data files into a list. Im going to be changing the class names a bit to reflect their usage better.

Im going to adopt seraphs approach and see how that works out. With a few alterations, it looks like the polymorphic way I was searching for.

Ok! I think I have a handle on this now, and it seems to do exactly as I want and is easily extendable! Here is my code now.

I created a generic EncounterFactory to be able to handle any type of encounter I want. Even though all the encounters inherit from a base Encounterable, I still need to spit out concrete version(Mission, Loot, etc).


public class EncounterFactory<T>
{
    private RandomizedList<T> randomizedList = new RandomizedList<T>();
    private IEncounterStrategy<T> strategy;

    public EncounterFactory(IEncounterStrategy<T> strategy)
    {
        this.strategy = strategy;
        string[] files = Reader.GetXmlFiles(strategy.GetPath());
        strategy.Create(files, randomizedList);
        randomizedList.Shuffle();
    }

    public T GetNext()
    {
        return randomizedList.Next();
    }
}

This is the generic IEncounterStrategy interface. Pretty simple.


public interface IEncounterStrategy<T>
{
    void Create(string[] files, RandomizedList<T> list);
    T GetNext();
    string GetPath();
}

Here is an example of the MissionStrategy class for loading up all the mission encounters from the xml files. All I need to do is create a new strategy class for each encounter type I want to add. This should make it easy for me to add new encounter types later on.


public class MissionStrategy : IEncounterStrategy<Mission>
{
    private string path = "Assets/Resources/XML Files/Activities/Missions";

    public MissionStrategy()
    {
    }

    public void Create(string[] files, RandomizedList<Mission> randomizedList)
    {
     // for (int i = 0; i < files.Length; i++)
     // {
     //      ...some code to extract information from the xml file
     //      Mission mission = new Mission(...all the data extracted from the xml file...)
     //      randomizedList.Add(mission);
     // }
    }

    public string GetPath()
    {
        return path;
    }
}

And here is how I create the factory & get a new mission out of the factory.


        EncounterFactory<Mission> missionFactory = new EncounterFactory<Mission>(new MissionStrategy());
        missionFactory.GetNext();

I'm pretty happy with the result. Hopefully I did this right, as it seems to work fine!

You are completely on the right track there just a few hints though you don't need to create a Factory to do the factory patterns construction functions are enough, as long as they are globally available.

This is my way of approaching this problem if you are interested in seeing it. It uses the reflection stuff that C# gives me to create the objects and after that the objects just take over the deserialisation of the node to initialise themselves from.
public class UIEntityReader : IIncludeFileReader
    {
        public UIEntityReader(String fileName, Game game) : base(fileName)
        {
            m_game = game;
        }

        protected override void processCurrentFile()
        {
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load(m_stream);

            XmlNode node = xmlDocument.FirstChild.NextSibling.FirstChild;

            while (node != null)
            {
                if (node is XmlNode && !(node is XmlComment))
                {
                    if (node.Name == "_include")
                    {
                        //We have just found another file we need to process for entity reading
                        XmlAttribute fileAttribute = node.Attributes["file"];
                        if (fileAttribute != null)
                        {
                            addFile(Directory.GetCurrentDirectory() + "\\Content\\ui\\screens\\" + fileAttribute.Value);
                        }
                    }
                    else
                    {
                        //Node should be a Entity
                        Entity entity = ObjectFactory.createEntity(m_game, node);
                        entity.Active = false;
                        entity.deserialise(node);
                        if (node.HasChildNodes)
                        {
                            XmlNode childNode = node.FirstChild;
                            while (childNode != null)
                            {
                                //Child nodes are either Attributes or Components
                                if (childNode.Name.Contains("Attribute"))
                                {
                                    IAttributeHandle handle;
                                    DebugWrapper.Assert(childNode.Attributes["name"] != null, "Attribute xml node has no \"name\" attribute" );
                                    String attributeName = childNode.Attributes["name"].Value;
                                    if (!entity.HasAttribute(attributeName))
                                    {
                                        handle = ObjectFactory.CreateAttributeHandle(m_game, childNode);
                                        entity.AddAttribute(handle);
                                    }
                                    else
                                    {
                                        handle = entity.getAttribute(attributeName);
                                    }

                                    if (handle != null)
                                    {
                                        handle.deserialise(childNode);
                                    }
                                }
                                else if (childNode.Name.Contains("Component"))
                                {
                                    ComponentManager compManRef = ComponentManager.getInstance(m_game);
                                    ComponentHandle temp = compManRef.GetComponent(childNode.Name);
                                    if (temp == null)
                                    {
                                        //Component doesn't exist yet so create one and add it to the manager
                                        Component component = ObjectFactory.CreateComponent(m_game, childNode);
                                        DebugWrapper.Assert(component != null, "Failed to create component: {0}", childNode.Name);
                                        component.deserialise(childNode);
                                        compManRef.AddComponent(new ComponentHandle(childNode.Name, component));
                                    }
                                    entity.AddComponent(childNode.Name);
                                }
                                else
                                {
                                    DebugWrapper.Warn("Found a child node with an unrecognized type {0}", childNode.Name);
                                }

                                childNode = childNode.NextSibling;
                            }
                        }

                        GameObjectManager.getInstance(m_game).AddGameObject(entity);
                    }

                    node = node.NextSibling;
                }
                else
                {
                    node = node.NextSibling;
                }
            }
        }

        private Game m_game;
    }

public class ObjectFactory
    {
        public static object constructObjectFromString(Game game, string typeName)
        {
            var type = getReflectionType(typeName);
            if (type != null)
            {
                return createGameObjectFromType(game, type);
            }

                return null;
        }

        public static Object constructObjectFromXmlNode(Game game, XmlNode node)
        {
            var type = getReflectionType(node);

            if (type != null)
            {
                return createGameObjectFromType(game, type);
            }

            return null;
        }

        public static Entity createEntity(Game game, XmlNode node)
        {
            var entityType = getReflectionType(node);
            if (entityType == null) return null;

            return (Entity)createOneParamObjectFromType<Game>(game, entityType);
        }

        public static Component CreateComponent(Game game, XmlNode node)
        {
            var componentType = getReflectionType(node);
            if (componentType == null) return null;

            return (Component)createOneParamObjectFromType<Game>(game, componentType);
        }

        public static IAttributeHandle CreateAttributeHandle(Game game, XmlNode node)
        {
            var attributeType = getReflectionType(node);
            if (attributeType == null) return null;

            Object obj = createZeroParamObjectFromType(attributeType);
            if (UtilFunctions.isDerivedType(typeof(IAttributeHandle), obj.GetType()))
            {
                return (IAttributeHandle)obj;
            }

            return null;
        }

        public static IAttributeHandle CreateAttributeHandle(Game game, Type genericType)
        {
            String typeName = "AttributeHandle";
            switch (Type.GetTypeCode(genericType))
            {
                case TypeCode.Int32:
                    typeName += "Int";
                    break;
                case TypeCode.Single:
                    typeName += "Float";
                    break;
                case TypeCode.Boolean:
                    typeName += "Bool";
                    break;
                case TypeCode.Byte:
                    typeName += "Byte";
                    break;
                case TypeCode.Char:
                    typeName += "Char";
                    break;
                case TypeCode.String:
                    typeName += "String";
                    break;
            }

            Type typeToConstruct = getReflectionType(typeName);
            if (typeToConstruct != null)
            {
                return (IAttributeHandle)createZeroParamObjectFromType(typeToConstruct);
            }

            return null;
        }

        public static ComponentHandle createComponentHandle(String type)
        {
            Type typeToConstruct = getReflectionType(type);
            return (ComponentHandle)createZeroParamObjectFromType(typeToConstruct);
        }

        public static Model constructModelFromFileName(Game game, Type modelType, Type modelReader, String fileName)
        {
            var modelObject = modelType != null ? createGameObjectFromType(game, modelType) : null;
            if (modelObject != null)
            {
                var modelReaderObject = modelReader != null ? createOneParamObjectFromType<IModelReader, String>(fileName, modelReader) : null;
                if (modelReaderObject != null)
                {
                    return modelReaderObject.loadFile(game);
                }
                else
                {
                    //Assume this is a XNA content pipeline object and load it in that way
                    ContentManager content = new ContentManager(game.Services, "Content");
                    return new FBXModel(content.Load<Microsoft.Xna.Framework.Graphics.Model>(fileName), game);
                }
            }
            return null;
        }

        public static object createGameObjectFromType(Game game, Type type)
        {
            return createOneParamObjectFromType<Game>(game, type);
        }

        public static Object constructGenericParamType(Type objectType, Type genericType)
        {
            Type[] typeArgs = { genericType };
            Type constructed = objectType.MakeGenericType(typeArgs);
            return  Activator.CreateInstance(constructed);
        }

        public static Object createZeroParamObjectFromType(Type type)
        {
            Type[] constructorParams = { };
            var constructor = type.GetConstructor(constructorParams);
            Object[] constructionParams = { };
            return constructor.Invoke(constructionParams);
        }

        public static Object createOneParamObjectFromType<T>(T obj, Type type)
        {
            Type[] constructorParams = { typeof(T) };
            var constructor = type.GetConstructor(constructorParams);
            Object[] constructionParams = { obj };
            return constructor.Invoke(constructionParams);
        }

        public static ReturnT createOneParamObjectFromType<ReturnT, T>(T obj, Type type)
        {
            Type[] constructorParams = { typeof(T) };
            var constructor = type.GetConstructor(constructorParams);
            Object[] constructionParams = { obj };
            return (ReturnT)constructor.Invoke(constructionParams);
        }

        public static Object createTwoParamObjectFromType<T, S>(T obj, S obj2, Type type)
        {
            Type[] constructorParams = { typeof(T), typeof(S) };
            var constructor = type.GetConstructor(constructorParams);
            Object[] constructionParams = { obj, obj2 };
            return constructor.Invoke(constructionParams);
        }

        public static ReturnT createTwoParamObjectFromType<ReturnT, T, S>(T obj, S obj2, Type type)
        {
            Type[] constructorParams = { typeof(T), typeof(S) };
            var constructor = type.GetConstructor(constructorParams);
            Object[] constructionParams = { obj, obj2 };
            return (ReturnT)constructor.Invoke(constructionParams);
        }

        public static Object createThreeParamObjectFromType<T, S, R>(T obj, S obj2, R obj3, Type type)
        {
            Type[] constructorParams = { typeof(T), typeof(S), typeof(R) };
            var constructor = type.GetConstructor(constructorParams);
            Object[] constructionParams = { obj, obj2, obj3 };
            return constructor.Invoke(constructionParams);
        }

        private static Type getReflectionType(string typeName)
        {
            var type = Type.GetType(typeName);
            if (type == null)
            {
                type = Type.GetType("SpaceSimulator." + typeName);
            }
            return type;
        }

        private static Type getReflectionType(XmlNode node)
        {
            return getReflectionType(node.Name);
        }
    }

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion

This topic is closed to new replies.

Advertisement