Object Oriented Programming. Importance of data types.

Anyone who starts reading about OOP or watches video tutorials about the subject knows that learning curve is very steep. Even if you have some experience of creating your own classes and objects, you still may not be fully comfortable creating complex projects. In fact some of the developers will try to limit number of new objects and classes for sake of avoiding complexity. I hope that after reading this article you will gain enough confidence to tackle OOP on your own.

It all starts with terms like inheritance and polymorphism. Then you are adding new definitions. Soon it feels like things are mushrooming and entire OOP concept is overwhelming. Learning about classes and object is not enough. You need to understand how objects talk to each other. And to make things even more interesting we have Design Patterns that should help us avoid some common mistakes.

Before you begin, you should have some basic understanding of what is data type. Not to overcomplicate things we can have string that represent words, int represent numbers and bool represents true or false. We could also have types that represent objects and classes. You should also be familiar with term constructor and what it does. It’s method that does not return any type, but instead defines object that we want to instantiate. Constructor method has the same name as a class name.

Below is a very simple method called DoSomething ().


public string DoSomething (string name) 
{
	return name + “ hello world”;
}

If you are not familiar with writing methods in C#, then you are probably asking yourself question why word string is repeated twice? It is because this particular method not only takes string as parameter inside a bracket, but it also returns string as output. Therefore the first string is a return type.

If I would ask you to rank importance of each element of this method, what order would you give? Maybe it would look like this.

1. DoSomething
2. name
3. string

Out of these three elements only one will remain constant. I can rename the method to SomethingElse(). I can replace “name” with another word. However if I delete both string types, than I do not need anything inside the brackets and there is no need for this method to return any value.

I have to admit that I was not fully aware of importance of types in writing the code. I read about interfaces. This definition of interfaces comes from MSDN website.

“An interface is like an abstract base class. Any class or struct that implements the interface must implement all its members. An interface can’t be instantiated directly. Its members are implemented by any class or struct that implements the interface. Interfaces can contain events, indexers, methods, and properties.”

There is a lot of going on in this definition. We have information about what can be included inside an interface and that it must be implemented by a class that wants to use it. I think our attention is completely drawn to this idea and we have this sort of physical representation of what interface is.

Sometimes interface will be compared to auto parts that fit together or power plug that goes to wall outlet. That power outlet with some extension cord is actually good example. Our plug needs to comply with physical design. We can chain multiple extension cords together because all of them conform to requirement. As I said before, we are probably so much into plugin our cords together that we dismiss importance of what is actually being passed between them, which is electric current. So there you have it, the type that would work with extension cord is electric current.

This is an example of an interface with one method called LogEntry ()


public interface ILogger
{
    string LogEntry ();
} 

Of course any class that would like to use this interface would have to implement this method. Right now method is called LogEntry (), but I could rename it. However it is far more important for this method to return type string than anything else. Otherwise, it would not be possible to write any entry to our log file.

I think you are starting to get an idea about importance of data types in designing our projects. We could have multitude of objects and classes and absolutely no idea how they work together. We could get lost in searching for method names and interfaces. But as soon we shift our attention to types that are being passed and returned between various methods, we gain clarity of purpose.

I would like to demonstrate this idea with two different examples that ultimately will give the same output. They both approach issue differently and both of them use different types. We start with this line of code:


var selectedMovies = movies.GetByYear(2000).GetById(1).GetByGenre("Action");

We want to filter out some movie collection based on certain criteria. First filter would get us all movies by year. Second filter would get movies with some group id number and finally third filter would return all action movies.

We have two classes that remain unchanged, Movie class and Repository class.


public class Movie
    {
        public string Title { get; set; }
        public int Year { get; set; }
        public int GroupId { get; set; }
        public string Genre { get; set; }

    } 



public  class Repository
    {

        private List localDb;

        public Repository()
        {
            localDb = new List();


        }


        public IEnumerable GetAllMovies()
        {
            localDb = new List();

            var movie1 = new Movie() { Title = "Movie1", Year = 2000, GroupId = 1, Genre = "Action" };
            var movie2 = new Movie() { Title = "Movie2", Year = 1999, GroupId = 1, Genre = "Drama" };
            var movie3 = new Movie() { Title = "Movie3", Year = 2000, GroupId = 1, Genre = "Comedy" };
            var movie4 = new Movie() { Title = "Movie4", Year = 2000, GroupId = 2, Genre = "Action" };
            var movie5 = new Movie() { Title = "Movie5", Year = 1999, GroupId = 2, Genre = "Drama" };
            var movie6 = new Movie() { Title = "Movie6", Year = 1999, GroupId = 2, Genre = "Drama" };
            var movie7 = new Movie() { Title = "Movie7", Year = 1999, GroupId = 2, Genre = "Horror" };


            localDb.Add(movie1);
            localDb.Add(movie2);
            localDb.Add(movie3);
            localDb.Add(movie4);
            localDb.Add(movie5);
            localDb.Add(movie6);
            localDb.Add(movie7);


            return localDb;
        }
    }


The first approach is to utilize a class that we call Library. Its constructor will take a List of Movie as parameter and hold its data inside a private list. We have two methods GetByYear () and GetById () that return Library object and one method GetByGenre() that returns IEnumerable list of Movie. It’s important that GetByYear () and GetById () return Library object, because that gives them access to any other methods defined inside Library class. And something else interesting is happening here, two Library object talk to each other through constructor and both of them have access to a private list that otherwise is not visible to other objects.



public  class Library
    {
        private IEnumerable MoviesLibrary;

        public Library(IEnumerable movies)
        {

            this.MoviesLibrary = movies.ToList();

        }

        public Library GetByYear(int year)
        {
            return new Library(this.MoviesLibrary.Where(movie => movie.Year == year));


        }

        public Library GetById(int id)
        {
            return new Library(this.MoviesLibrary.Where(movie => movie.GroupId == id));


        }


        public IEnumerable GetByGenre(string genre)
        {
            return this.MoviesLibrary.Where(movie => movie.Genre == genre);

        }


        public void Display()			
        {
		
            foreach (var movie in this.MoviesLibrary)
            {
Console.WriteLine("Title: {0} , Year {1}, Group: {2}, Genre: {3}",	 movie.Title,movie.Year,movie.GroupId, movie.Genre);
            }



        }

    } 


Now let’s try something different. Instead of Library class we can create extension methods that would provide similar functionality. Extension methods differ from regular methods that they require word “this” inside a brackets. For example “this Repository” means that this method will work with Repository type and “this IEnumerable of Movie” can be used with any IEnumerable list of Movie. Also notice that types that are being returned by these methods. GetByYear () , GetById(), and GetByGenre () return IEnumerbale list of Movie. Returning type can be passed as parameter to another method.




public static class Extensions
{

	public static IEnumerable GetByYear(this Repository repository, int year)
	{
	
		return repository.GetAllMovies().Where(p=>p.Year == year).Select(p => p);
	
	}

	
	public static IEnumerable GetById(this IEnumerable movies, int groupId)
	{
	
		return movies.Where(p=>p.GroupId == groupId).Select(p => p);
	
	}
	
	public static IEnumerable GetByGenre(this IEnumerable movies, string genre)
	{
	
		return movies.Where(p=>p.Genre == genre).Select(p => p);
	
	}

}


Finally this how we will use our classes

Library class:


var repository = new Repository();
var listOfMovies = repository.GetAllMovies();
var movies = new Library(listOfMovies);
		
var selectedMovies = movies.GetByYear(2000).GetById(1).GetByGenre("Action");

foreach(var movie in selectedMovies)
{
Console.WriteLine("Title: {0} , Year {1}, Group: {2}, Genre: {3}", movie.Title,movie.Year,movie.GroupId, movie.Genre);
}


Output: Title: Movie1 , Year 2000, Group: 1, Genre: Action

Extension methods:


var repository = new Repository()
var selectedMovies  = repository.GetByYear(2000).GetById(1).GetByGenre("Action");
		
foreach(var movie in selectedMovies)
{
Console.WriteLine("Title: {0} , Year {1}, Group: {2}, Genre: {3}", movie.Title,movie.Year,movie.GroupId, movie.Genre);
}

Output: Title: Movie1 , Year 2000, Group: 1, Genre: Action

As you can see we have some flexibility when comes to writing our code and as long we understand what types are being passed and returned we should be confident in our design choices. I would strongly recommend before even writing any lines of code to think through some basic structure of classes and objects required. In addition, we have to be flexible too. Sometimes what looks great on paper maybe complete disaster when comes to performance and we have to quickly adjust our design. At that point it is far easier to come to a solution when we shift our attention to types that are required and passed between our objects.

Leave a Reply

Your email address will not be published. Required fields are marked *