Interfaces Example

Interfaces are the third pillar of OOP programming. Interfaces give you a way to implements certain functionality without knowing how or what do exactly. Think of it as a high level blueprint on how you want the program to run. Just remember that if you inherit from an interface you have to implement every method, class, data type, etc.., defined in the interface. Also the reverse is true that if you try and implement functionality not defined in the interface you will run into compiler errors.

Click HERE for the complete program source code.

InterfacesProgram

 
IGradeTracker.cs code contents

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _1.Grades
{
    public interface IGradeTracker
    {
        void AddGrade(float grade);
        GradeStatistics ComputeStatictics();
        void WriteGrades(TextWriter textWriter);
        event NameChangedDelegate NameChanged;
        string Name { get; set; }
        void DoSomething();
    }
}

This interfaces has various items that need to be defined if anything choses to inherit from it. Currently there are some void methods, an event delagate, a string, and a GradeStatistics object. Like I sais earlier this interface is a blueprint of what needs to be done. How it gets done is up to the programmer implementing the interface. One of the biggest advantages of using an interface is you can inherit from more than one. Unlike class inheritance where you can only inherit from one other class.

 
GradeTracker.cs code contents

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _1.Grades
{
    public abstract class GradeTracker : IGradeTracker
    {
        // Private Fields.
        //
        private string _name;
        
        // Public Fields.
        //
        public abstract void AddGrade(float grade);
        public abstract GradeStatistics ComputeStatictics();
        public abstract void WriteGrades(TextWriter textWriter);
        public event NameChangedDelegate NameChanged;
        public abstract void DoSomething();

        // Object Constructors.
        //
        public string Name
        {
            get
            {
                // If the name of the book is invalid
                if (String.IsNullOrEmpty(_name))
                    return "No Book Name Was Given";
                else
                    return _name;
            }
            set
            {
                // Checking to make sure you can change the name to null or empty.
                if (String.IsNullOrEmpty(value))
                {
                    throw new ArgumentException("The name cannot be null or empty.");
                }

                if (_name != value)
                {
                    // Need to do something that announces that the name has changed.
                    var oldNameValue = _name;   // This is to capture the old value before it changes.
                    _name = value;

                    // Do some checking to keep an exception from happening.
                    if (NameChanged != null)
                    {
                        // Creating the event args delegate object.
                        NameChangedEventArgs args = new NameChangedEventArgs();
                        args.OldValue = oldNameValue;
                        args.NewValue = value;
                        NameChanged(this, args);    // Passing this and the args object as parameters.
                    }
                }
            }
        }
    }
}

The GradeTracker class inherits from the IGradeTracker and implements all of it’s properties. I’ve made some of the fields abstract so they can be overridden. If you noticed the constructor looks similar to the contructor in the GradeBook class. That’s because I wanted to have the same properties but inherting from an interface so certain things have to be done.

 
GradeBook.cs code contents

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _1.Grades
{
    public class GradeBook : GradeTracker
    {
        // Protected Fields.
        //
        protected List<float> _grades;

        // Public fields.
        //

        // Constructors.
        //
        public GradeBook()
        {
            Console.WriteLine("Running From The GradeBook Class Without A Parameter");
            _grades = new List<float>();
        }

        public GradeBook(string name)
        {
            Console.WriteLine("Running From The GradeBook Class With A Parameter");
            _grades = new List<float>();
        }

        public override void DoSomething()
        {
            
        }

        // Public methods
        //
        public override void AddGrade(float grade)
        {
            // Adding some validation logic before adding the grade to the collection.
            if (grade >= 0 && grade <= 100)
                _grades.Add(grade);
        }

        // Overridable method that computes the grade stats.
        public override GradeStatistics ComputeStatictics()
        {
            Console.WriteLine("Compute Statistics Running From The GradeBook Class.");

            // Creating an instance variable of the class to return.
            GradeStatistics stats = new GradeStatistics();

            // Now to find the average, highest, and lowest grade in the list.
            float sum = 0f;
            foreach (float grade in _grades)
            {
                // Calculating the highest and lowest grade using built-in .NET methods.
                // Comparing the current grade in the loop to the highest grade. If greater then assign the new value.
                stats.HighestGrade = Math.Max(grade, stats.HighestGrade);

                // Comparing the current grade in the loop to the lowest grade. If lower then assign the new value.
                stats.LowestGrade = Math.Min(grade, stats.LowestGrade);
                
                // Adding numbers to the sum variable.
                sum += grade;
            }

            // Calculating the average grade.
            stats.AverageGrade = sum / _grades.Count();     // Normally this would be incased in a try/catch. Possible divide by zero exception.
            return stats;
        }
   
        public override void WriteGrades(TextWriter textWriter)
        {
            // Demonstrating the different ways to iterate using the _grades List.
            // Uncomment the different loops to see what happens.

            // Using a foreach loop.
            textWriter.WriteLine("Grades:");
            foreach (float grade in _grades)
            {
                textWriter.WriteLine(grade);
            }
            textWriter.WriteLine("*************************");

            // Using a for loop.
            /*
            textWriter.WriteLine("Grades:");
            for (int i = 0; i < _grades.Count; i++) { textWriter.WriteLine(_grades[i]); } textWriter.WriteLine("*************************"); */ // Using a for loop to display the list in reverse. /* textWriter.WriteLine("Grades:"); for (int i = _grades.Count - 1; i >= 0; i--)
            {
                textWriter.WriteLine(_grades[i]);
            }
            textWriter.WriteLine("*************************");
             */

            // Using a while loop.
            /*
            textWriter.WriteLine("Grades:");
            int i = 0;
            while (i < _grades.Count)
            {
                textWriter.WriteLine(_grades[i]);
                i++;
            }
            textWriter.WriteLine("*************************");
             */

            // Using a Do/While loop.
            /*
            textWriter.WriteLine("Grades:");
            int i = 0;
            do
            {
                textWriter.WriteLine(_grades[i]);
                i++;
            } while (i < _grades.Count);
            textWriter.WriteLine("*************************");
             */
        
        }
    }
}

Following the inheritance rule I’m making the GradeBook class inherit from the GradeTracker class which inherits from the IGradeTracker interface. From here I will be implementing the methods defined in the GradeTracker class and the IGradeTracker interface. The nature of interfaces forces me to implement the defined methods, properties, etc…

 
Program.cs code contents

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _1.Grades
{
    class Program
    {
        static void Main(string[] args)
        {
            // Demonstrating Interface classes.
            IGradeTracker book = CreateGradeTracker();
            //IGradeTracker book = ThrowAwayGrade();

            // Testing the way Interfaces work.
            book.DoSomething();

            // Working the exceptions with try/catch
            try
            {
                Console.WriteLine("Enter A Name For The Book");
                book.Name = Console.ReadLine();
            }
            catch (ArgumentException ex)
            {
                Console.WriteLine("That Book Name Isn't Valid.");
            }

            // Working with a text file to pull grades from.
            // Also using a try/catch for this unit of work.
            try
            {
                // Using statement so file streaming to read the file.
                // Since these classes use dispose no need to call on that method manually.
                using (FileStream stream = File.Open("grades.txt", FileMode.Open))
                using (StreamReader reader = new StreamReader(stream))
                {
                    string line = reader.ReadLine();
                    while (line != null)
                    {
                        // Checking to make sure only numbers are read without throwing an exception.
                        float grade;
                        if (float.TryParse(line, out grade))
                        {
                            book.AddGrade(grade);
                        }
                        line = reader.ReadLine();
                    }
                }
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine("Cannot locate the {0} file.", ex.FileName);
                return;
            }

            // Working with iterating / looping.
            book.WriteGrades(Console.Out);

            // Testing the use of the name changed delegate.
            /*
            book.NameChanged += new NameChangedDelegate(OnNameChanged);
            book.Name = "Changed Book Name";
             */

            // Calling a method that will compute statistics and return me a value.
            // Then that value will be stored in a class variable.
            GradeStatistics stats = book.ComputeStatictics();
            
            WriteNames(book.Name);
            Console.WriteLine(stats.HighestGrade);
            Console.WriteLine(stats.LowestGrade);
            Console.WriteLine(stats.AverageGrade);
            Console.WriteLine("{0} {1}", stats.AverageLetterGrade, stats.GradeDescription);
            Console.ReadLine();
        }

        // Method that creates a grade book interface object from the CreateGradeBook Class.
        private static IGradeTracker CreateGradeTracker()
        {
            IGradeTracker book = new GradeBook("Daniels Book");
            return book;
        }

        // Method that created a grade book interface object from the ThrowAwayGrade book class.
        private static IGradeTracker ThrowAwayGrade()
        {
            IGradeTracker book = new ThrowAwayGrade("Daniels Book");
            return book;
        }

        // Delegate method used to display what the name has changed to.
        private static void OnNameChanged(object sender, NameChangedEventArgs args)
        {
            Console.WriteLine("Name Changed From {0} to {1}", args.OldValue, args.NewValue);
        }

        // Method used to convert float value into bytes and add to an array.
        private static void WriteBytes(float value)
        {
            // Using the bit converter add the value to the byte array.
            byte[] bytes = BitConverter.GetBytes(value);

            FormatBytes(bytes);
        }

        // Method used to convert integer values to bytes and add to an array.
        private static void WriteBytes(int value)
        {
            // Using the bit converter add the value to the byte array.
            byte[] bytes = BitConverter.GetBytes(value);

            FormatBytes(bytes);
        }

        // Refactored method to display the values in the byte array.
        private static void FormatBytes(byte[] bytes)
        {
            // Loop thru the array to get teh values.
            foreach (byte b in bytes)
            {
                Console.Write("0x{0:X} ", b);
            }
            Console.WriteLine();
        }

        // Demonstrating how the params keyword can be used.
        private static void WriteNames(params string[] names)
        {
            foreach (string name in names)
            {
                Console.WriteLine("The Current Book Name Is: {0}", name);   
            }
        }
    }
}

In the main program class I’ll be calling on a method that creates and object of IGradeTacker. From there I can use the defined methods to add grades from the text file, remove the lowest grade, etc… By forcing the use of an interface I’m introducing some type safe ways of doing things.

I didn’t post the code for the ThrowAwayGrade class because nothing changed from the blog post on PolyMorphism. If you want to see code look there. Or download the source of this program.

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s