Page 1 of 1

Simple tutorial: Exploring interfaces.

#1 JacobH   User is offline

  • D.I.C Head
  • member icon

Reputation: 26
  • View blog
  • Posts: 181
  • Joined: 07-September 15

Posted 16 April 2016 - 02:41 PM

Hello there,

I am going to keep this tutorial short-and-sweet with most of the learning hopefully coming from looking at code and brief remarks about it. Hopefully you leave the thread feeling a bit better about interfaces than you did before reading it. So lets just start right away and design a basic set of interfaces.

The goal is to make a design that allows quick and easy loading/saving/reading of custom settings. Now here is a question that really highlights the importance of object oriented programming in general, and as a bonus, the value of interfaces. What settings format do we want to design our system for? Json? XML? Ini? Some settings format we do not even know we want yet? How about one that does not even exist yet? The answer is.. none of the above.

That is the beauty of core object oriented programming. Solid interface design allows us to not care about the questions above. You can change the implementation, not the design. Changing your entire design 10 years into the applications life is not an easy task. Changing the implementation of the way you load/save settings from JSON to XML is.


So lets go ahead and design it step by step.

Note, for all the classes here, the only namespaces you need to include are:

using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml.Serialization;



First, we need a way to load and save the settings and access its contents. Lets make this in of its self an inter-face as it is useful for a wide number of operations. We will define four basic operations, which the method names describe.

ISerializeTool

    public interface ISerializeTool
    {
        string ExportToString<T>(T obj);
        T ImportFromString<T>(string obj);
        void ExportToFile<T>(T obj, string file);
        T ImportFromFile<T>(string path);
    }



Great, now we have defined a contract of sorts to handle the data we want.

Now lets design a basic interface to be used to manage a settings file. We use the <out T>, which is just using covariance, which allows you to use a more derived type than that specified by the generic parameter.

ISettings

    public interface ISettings<out T>
    {
        T Current { get; }
        void Save(string target = "");
        void Load(string location = "");
    }



So there is our two interfaces. Now lets show how they can be used to implement a settings manager of sorts using the interfaces above.

Settings


    public class Settings<T> : ISettings<T>
    {
        public T Current { get; set; }

        public string DefaultPath { get; set; }

        public ISerializeTool SettingsSerializer { get; set; } 

        public void Save(string target = "")
        {
            if (string.IsNullOrEmpty(target))
                target = DefaultPath;

            SettingsSerializer.ExportToFile(Current, target);
        }

        public void Load(string location = "")
        {
            if (string.IsNullOrEmpty(location))
                location = DefaultPath;

            if(!File.Exists(location))
                throw new FileNotFoundException("Mydata settings file does not exist.");

            Current = SettingsSerializer.ImportFromFile<T>(location);
        }
    }



Now lets tie them all together for something useful. Lets just say we are feeling XML today and that is what we want our settings to use. No problem, we just need to implement the ISerializeTool. Here is mine:

    public class XmlSerializeTool : ISerializeTool
    {
        public string ExportToString<T>(T obj)
        {
            var serializer = new XmlSerializer(typeof (T));
            using (var stringWriter = new StringWriter())
            {
                serializer.Serialize(stringWriter, obj);
                return stringWriter.ToString();
            }
        }

        public T ImportFromString<T>(string serializedObj)
        {
            var serializer = new XmlSerializer(typeof (T));
            using (var stringWriter = new StringReader(serializedObj))
            {
                return (T) serializer.Deserialize(stringWriter);
            }
        }

        public void ExportToFile<T>(T obj, string file)
        {
            using (var fileWriter = new StreamWriter(file, false, Encoding.UTF8))
            {
                fileWriter.Write(ExportToString(obj));
            }
        }

        public T ImportFromFile<T>(string file)
        {
            using (var fileReader = new StreamReader(file, Encoding.UTF8))
            {
                return ImportFromString<T>(fileReader.ReadToEnd());
            }
        }
    }



Now, for examples sake, here is a simple test you can use to try all the above.

    public class RandomNameGeneratorSettings
    {
        public List<string> FirstNames { get; set; } 
        public List<string> LastNames { get; set; }
    }

    public static class SettingsTest
    {
        public static ISettings<RandomNameGeneratorSettings> SettingsManager { get; set; }

        public static void RunTest()
        {
            // Make settings.
            var settings = new Settings<RandomNameGeneratorSettings>
            {
                SettingsSerializer = new XmlSerializeTool(),
                DefaultPath = "MyPath.xml",
                Current = new RandomNameGeneratorSettings
                {
                    FirstNames = new List<string> {"Joe", "Bob"},
                    LastNames = new List<string> {"Johnson", ""}
                }
            };

            // Set the ISettings above with our implementations.
            SettingsManager = settings;

            // Save the settings to a file and load them from that file.
            SettingsManager.Save();
            SettingsManager.Load();

            // Print first names in current settings.
            foreach (var setting in SettingsManager.Current.FirstNames)
            {
               Console.WriteLine(setting);
            }

            // Add a name to the current settings.
            SettingsManager.Current.FirstNames.Add("Josh");

            // Save to (default) file again.
            SettingsManager.Save();

            // Clear the current first names.
            SettingsManager.Current.FirstNames.Clear();

            // Load the settings again from the default file, 
            // which should give us the new dat that we changed just before.

            SettingsManager.Load();
            // Ensure that happened.

            foreach (var setting in SettingsManager.Current.FirstNames)
            {
                Console.WriteLine(setting);
            }

        }
    }



I hope you have a better grasp on interfaces and C# after reading this. Good luck.

Is This A Good Question/Topic? 3
  • +

Replies To: Simple tutorial: Exploring interfaces.

#2 SixOfEleven   User is offline

  • Planeswalker
  • member icon

Reputation: 1055
  • View blog
  • Posts: 6,643
  • Joined: 18-October 08

Posted 26 July 2016 - 01:25 PM

Great example of core object-oriented design. Your classes have a well defined public contract to interact with them and the external classes do not need to understand the implementation of the logic in the other classes in order to make use of them.

I sadly find that interfaces are generally under used/misunderstood in C# by a lot of programmers.
Was This Post Helpful? 0
  • +
  • -

#3 JacobH   User is offline

  • D.I.C Head
  • member icon

Reputation: 26
  • View blog
  • Posts: 181
  • Joined: 07-September 15

Posted 26 July 2016 - 07:36 PM

I think this is because interfaces seem to have a fine line between being great or being just massive over-architecture. You either define a well designed set of public API that feeds off others, such as ICollection, or you design very light weight ones for a job using the same methods you will use very frequently, such as settings (99% of release projects use some form of them). Combined with bad design even when the intentions are actually good to begin with, it can just feel like interfaces suck.
Was This Post Helpful? 0
  • +
  • -

#4 wtp   User is offline

  • D.I.C Regular

Reputation: 28
  • View blog
  • Posts: 334
  • Joined: 08-December 11

Posted 11 October 2016 - 07:11 AM

Can you put this on github?
Was This Post Helpful? 0
  • +
  • -

#5 JacobH   User is offline

  • D.I.C Head
  • member icon

Reputation: 26
  • View blog
  • Posts: 181
  • Joined: 07-September 15

Posted 11 October 2016 - 05:50 PM

What exactly? The entire tutorial?

In this project link, look at the files below.. they apply all these concepts.

https://github.com/l...rlay.NET/Common



XmlSerializerEx.cs
ISettings.cs
ISerializer.cs
SerializableSettings.cs
Was This Post Helpful? 1
  • +
  • -

#6 bluemarmalade   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 19
  • Joined: 05-July 15

Posted 29 September 2017 - 01:18 AM

Hey, nice tutorial! I remember i loved how this explained interfaces so i bookmarked this page. Now returning I see that the hidden content can't be revealed by clicking those buttons. Could you fix this?
Was This Post Helpful? 0
  • +
  • -

#7 calljulie369   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 5
  • Joined: 31-January 18

Posted 31 January 2018 - 10:53 PM

Very detailed Interface Tutorial.

This post has been edited by andrewsw: 31 January 2018 - 11:08 PM
Reason for edit:: Removed previous quote, just press REPLY

Was This Post Helpful? 0
  • +
  • -

#8 andrewsw   User is offline

  • RequestedRangeNotSatisfiable
  • member icon

Reputation: 6552
  • View blog
  • Posts: 26,565
  • Joined: 12-December 12

Posted 31 January 2018 - 11:09 PM

@calljulie369 There is no need to quote the tutorial in full, there is a REPLY button further down the page.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1