© Vaskaran Sarcar 2020
V. SarcarDesign Patterns in C#https://doi.org/10.1007/978-1-4842-6062-3_26

26. MVC Pattern

Vaskaran Sarcar1 
(1)
Garia, Kolkata, West Bengal, India
 

This chapter covers the MVC pattern.

Definition

MVC (model-view-controller) is an architectural pattern. This pattern is commonly used in web applications and in developing powerful user interfaces. Trygve Reenskaug first described MVC in 1979 in a paper titled “Applications programming in Smalltalk-80TM: How to use Model-View-Controller,” which was written before the existence of the World Wide Web. So, at that time, there was no concept of web applications. But modern-day applications are an adaptation of that original concept. Instead of treating it a true design pattern, some developers prefer to say this an “MVC architecture.”

Wikipedia defines it as follows.

Model-view-controller (MVC) is an architectural pattern commonly used for developing user interfaces that divides an application into three interconnected parts. This is done to separate internal representations of information from the way information is presented to and accepted by the user. The MVC design pattern decouples these major components allowing for efficient code reuse and parallel development. (https://en.wikipedia.org/wiki/Model-view-controller)

My favorite description of MVC comes from Connelly Barnes, who said,

An easy way to understand MVC: the model is the data, the view is the window on the screen, and the controller is the glue between the two. (http://wiki.c2.com/?ModelViewController)

Concept

Using this pattern, you separate the user interface logic from the business logic and decouple the major components in such a way that those can be reused efficiently. This approach promotes parallel development.

From the definition, it is apparent that the pattern consists of these major components: model, view, and controller. The controller is placed between the view and model in such a way that they can communicate with each other only through the controller. This model separates the mechanism for how the data is displayed from the mechanism for how the data is manipulated. Figure 26-1 shows the MVC pattern.
../images/463942_2_En_26_Chapter/463942_2_En_26_Fig1_HTML.jpg
Figure 26-1

A typical MVC architecture

Key Points to Remember

These are brief descriptions of the key components in this pattern.
  • View represents the final output. It can also accept user input. It is a presentation layer, and you can think of it as a graphical user interface (GUI). You can design it with various technologies. For example, in a .NET application, you can use HTML, CSS, WPF, and so on, and for a Java application, you can use AWT, Swing, JSF, JavaFX, and so forth.

  • The model manages the data and business logic, and it acts as the actual brain of your application. It manages the data and business logic. It knows how to store, manage, or manipulate the data and handle the requests that come from the controller. But this component is separated from the view component. A typical example is a database, a file system, or a similar kind of storage. It can be designed with Oracle, SQL Server, DB2, Hadoop, MySQL, and so on.

  • The controller is the intermediary. It accepts a user’s input from the view component and passes the request to the model. When it gets a response from the model, it passes the data to a view. It can be designed with C# .NET, ASP.NET, VB.NET, Core Java, JSP, Servlets, PHP, Ruby, Python, and so on.

You may notice varying implementations in different applications. Here are some examples.
  • You can have multiple views.

  • Views can pass runtime values (for example, using JavaScript) to controllers.

  • Your controller can validate the user’s input.

  • Your controller can receive input in various ways. For example, it can get input from a web request via a URL, or input can be passed by clicking a Submit button on a form.

  • In some applications, you may notice that the model can update the view component.

In short, you need to use this pattern to support your own need. Figures 26-2, 26-3, and 26-4 show known variations of an MVC architecture.

Variation 1

Figure 26-2 shows variation 1.
../images/463942_2_En_26_Chapter/463942_2_En_26_Fig2_HTML.jpg
Figure 26-2

A typical MVC framework

Variation 2

Figure 26-3 shows variation 2.
../images/463942_2_En_26_Chapter/463942_2_En_26_Fig3_HTML.jpg
Figure 26-3

An MVC framework with multiple views

Variation 3

Figure 26-4 shows variation 3.
../images/463942_2_En_26_Chapter/463942_2_En_26_Fig4_HTML.jpg
Figure 26-4

An MVC pattern implemented with an Observer pattern/event-based mechanism

One of the best descriptions for MVC comes from wiki.​c2.​com (http://wiki.c2.com/?ModelViewController), where it says, “We need smart models, thin controllers, and dumb views.”

Real-World Example

Consider our Template Method pattern’s real-life example. But this time, let’s interpret it differently. I said that in a restaurant, based on customer input, a chef adjusts the taste and makes the final dish. But you know that the customers do not place their orders directly with the chef. The customers see the menu card (View), may consult with the waiter/waitress, and then place the order. The waiter passes the order slip to the chef who gathers the required materials from the restaurant’s kitchen (similar to storehouses or, computer databases). Once prepared, the waiter carries the plate to the customer’s table. So, you can consider the role of a waiter as the controller, the chefs in the kitchen as the model, and the food preparation materials as the data.

Computer-World Example

Many web programming frameworks use the concept of the MVC framework. Typical examples include Django, Ruby on Rails, ASP.NET, and so on. A typical ASP.NET MVC project can have the following view shown in Figure 26-5.
../images/463942_2_En_26_Chapter/463942_2_En_26_Fig5_HTML.jpg
Figure 26-5

Solution Explorer view of a typical ASP.NET MVC Project

Points to Note

Different technologies follow different structures, so you don't need a folder structure with the strict naming convention shown in Figure 26-5.

Implementation

For simplicity and to match our theory, I also divided the upcoming implementation into three major parts: model, view, and controller. Once you note the Solution Explorer view, you can identify the separate folders created to accomplish this task. Here are some important points.
  • IModel, IView, and IController are three interfaces that are implemented by the concrete classes EmployeeModel, ConsoleView, and EmployeeController, respectively. Seeing these names, you can assume that these are representatives of the model, view, and controller layers of our MVC architecture.

  • In this application, the requirement is very simple. Some employees need to register on an application. Initially, the application has three different registered employees: Amit, Jon, and Sam. These employees have ID’s as E1, E2, and E3. So, you see the following constructor:
    public EmployeeModel()
    {
        // Adding 3 employees at the beginning.
        enrolledEmployees = new List<Employee>();
        enrolledEmployees.Add(new Employee("Amit", "E1"));
        enrolledEmployees.Add(new Employee("John", "E2"));
        enrolledEmployees.Add(new Employee("Sam", "E3"));
    }
  • At any point in time, you should be able to see the enrolled employees in the system. In the client code, you invoke DisplayEnrolledEmployees() on a Controller object as follows:

controller.DisplayEnrolledEmployees();
Then the controller passes the call to view layer as follows:
view.ShowEnrolledEmployees(enrolledEmployees);
And you see that a concrete implementor of View Interface (ConsoleView.cs) describes the method as follows:
public void ShowEnrolledEmployees (List<Employee> enrolledEmployees)
{
        Console.WriteLine(" ***This is a console view of currently enrolled employees.*** ");
        foreach (Employee emp in enrolledEmployees)
        {
                Console.WriteLine(emp);
        }
        Console.WriteLine("---------------------");
}
  • You can add a new employee or delete an employee from the registered employees list. The AddEmployeeToModel(Employee employee) and RemoveEmployeeFromModel(string employeeIdToRemove) methods are used for this purpose. Let’s look at the method signature of RemoveEmployeeFromModel(...). To delete an employee, you need to supply the employee ID (which is nothing more than a string). But if the employee ID is not found, the application ignores this delete request.

  • A simple check is added in the Employee class to ensure that you are not adding an employee with the same ID repeatedly in the application.

Now go through the implementation. Yes, it’s big, but when you analyze it part by part with the help of the previous bullet points and the supporting diagrams, you should not face any difficulties with understanding the code. You can also consider the comments for your immediate reference.

Points to Note

Typically, you want to use MVC with technologies that offer built-in support and perform much of the groundwork. For example, when you use ASP.NET (or a similar technology) to implement the MVC pattern because you have a lot of built-in support. In these cases, you need to learn the new terminologies.

Throughout this book, I use console applications for design pattern implementations. Let’s continue to use the same for the upcoming implementation, because our focus is only on MVC architecture.

Class Diagram

Figure 26-6 shows the class diagram.
../images/463942_2_En_26_Chapter/463942_2_En_26_Fig6_HTML.jpg
Figure 26-6

Class diagram

Solution Explorer View

Figure 26-7 shows the high-level structure of the program.
../images/463942_2_En_26_Chapter/463942_2_En_26_Fig7_HTML.jpg
Figure 26-7

Solution Explorer view

Demonstration 1

Here is the complete demonstration.

Contents in Model folder

// Employee.cs
namespace MVCPattern.Model
{
    // The key "data" in this application
    public class Employee
    {
        private string empName;
        private string empId;
        public string GetEmpName()
        {
            return empName;
        }
        public string GetEmpId()
        {
            return empId;
        }
        public Employee(string empName, string empId)
        {
            this.empName = empName;
            this.empId = empId;
        }
        public override string ToString()
        {
            return  $"{empName} is enrolled with id : {empId}.";
        }
    }
}
// Model.cs
using System.Collections.Generic;
namespace MVCPattern.Model
{
    public interface IModel
    {
        List<Employee> GetEnrolledEmployeeDetailsFromModel();
        void AddEmployeeToModel(Employee employeee);
        void RemoveEmployeeFromModel(string employeeId);
    }
}
// EmployeeModel.cs
using System;
using System.Collections.Generic;
namespace MVCPattern.Model
{
    public class EmployeeModel : IModel
    {
        List<Employee> enrolledEmployees;
        public EmployeeModel()
        {
            // Adding 3 employees at the beginning.
            enrolledEmployees = new List<Employee>();
            enrolledEmployees.Add(new Employee("Amit", "E1"));
            enrolledEmployees.Add(new Employee("John", "E2"));
            enrolledEmployees.Add(new Employee("Sam", "E3"));
        }
        public List<Employee> GetEnrolledEmployeeDetailsFromModel()
        {
            return enrolledEmployees;
        }
        // Adding an employee to the model(registered employee list)
        public void AddEmployeeToModel(Employee employee)
        {
            Console.WriteLine($" Trying to add an employee to the registered list.The employee name is {employee.GetEmpName()} and id is {employee.GetEmpId()}.");
            if (!enrolledEmployees.Contains(employee))
            {
                enrolledEmployees.Add(employee);
                Console.WriteLine(employee + " [added recently.]");
            }
            else
            {
                Console.WriteLine("This employee is already added in the registered list.So, ignoring the request of addition.");
            }
        }
        // Removing an employee from model(registered employee list)
        public void RemoveEmployeeFromModel(string employeeIdToRemove)
        {
            Console.WriteLine($" Trying to remove an employee from the registered list.The employee id is {employeeIdToRemove}.");
            Employee emp = FindEmployeeWithId(employeeIdToRemove);
            if (emp != null)
            {
                Console.WriteLine("Removing this employee.");
                enrolledEmployees.Remove(emp);
            }
            else
            {
                Console.WriteLine($"At present, there is no employee with id {employeeIdToRemove}.Ignoring this request.");
            }
        }
        Employee FindEmployeeWithId(string employeeIdToRemove)
        {
            Employee removeEmp = null;
            foreach (Employee emp in enrolledEmployees)
            {
                if (emp.GetEmpId().Equals(employeeIdToRemove))
                {
                    Console.WriteLine($" Employee Found.{emp.GetEmpName()} has id: { employeeIdToRemove}.");
                    removeEmp = emp;
                }
            }
            return removeEmp;
        }
    }
}

Contents in View folder

// View.cs
using MVCPattern.Model;
using System.Collections.Generic;
namespace MVCPattern.View
{
    public interface IView
    {
        void ShowEnrolledEmployees(List<Employee> enrolledEmployees);
    }
}
// ConsoleView.cs
using System;
using System.Collections.Generic;
using MVCPattern.Model;
namespace MVCPattern.View
{
    public class ConsoleView : IView
    {
        public void ShowEnrolledEmployees(List<Employee> enrolledEmployees)
        {
            Console.WriteLine(" ***This is a console view of currently enrolled employees.*** ");
            foreach (Employee emp in enrolledEmployees)
            {
                Console.WriteLine(emp);
            }
            Console.WriteLine("---------------------");
        }
    }
}

Contents in Controller folder

// Controller.cs
using MVCPattern.Model;
namespace MVCPattern.Controller
{
    interface IController
    {
        void DisplayEnrolledEmployees();
        void AddEmployee(Employee employee);
        void RemoveEmployee(string employeeId);
    }
}
// EmployeeController.cs
using System.Collections.Generic;
using MVCPattern.Model;
using MVCPattern.View;
namespace MVCPattern.Controller
{
    public class EmployeeController : IController
    {
        IModel model;
        IView view;
        public EmployeeController(IModel model, IView view)
        {
            this.model = model;
            this.view = view;
        }
        public void DisplayEnrolledEmployees()
        {
            // Get data from Model
            List<Employee> enrolledEmployees = model.GetEnrolledEmployeeDetailsFromModel();
            // Connect to View
            view.ShowEnrolledEmployees(enrolledEmployees);
        }
        // Sending a request to model to add an employee to the list.
        public void AddEmployee(Employee employee)
        {
            model.AddEmployeeToModel(employee);
        }
        // Sending a request to model to remove an employee from the list.
        public void RemoveEmployee(string employeeId)
        {
            model.RemoveEmployeeFromModel(employeeId);
        }
    }
}

Client code

// Program.cs
using MVCPattern.Controller;
using MVCPattern.Model;
using MVCPattern.View;
using System;
namespace MVCPattern
{
    class Client
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***MVC architecture Demo*** ");
            // Model
            IModel model = new EmployeeModel();
            // View
            IView view = new ConsoleView();
            // Controller
            IController controller = new EmployeeController(model, view);
            controller.DisplayEnrolledEmployees();
            // Add an employee
            Employee empToAdd = new Employee("Kevin", "E4");
            controller.AddEmployee(empToAdd);
            // Printing the current details
            controller.DisplayEnrolledEmployees();
            // Remove an existing employee using the employee id.
            controller.RemoveEmployee("E2");
            // Printing the current details
            controller.DisplayEnrolledEmployees();
            /* Cannot remove an  employee who does not belong to the list.*/
            controller.RemoveEmployee("E5");
            // Printing the current details
            controller.DisplayEnrolledEmployees();
            // Avoiding a duplicate entry
            controller.AddEmployee(empToAdd);
            // Printing the current details
            controller.DisplayEnrolledEmployees();
            /* This segment is added to discuss a question in "Q&A Session" and initially commented out. */
           // view = new MobileDeviceView();
           // controller = new EmployeeController(model, view);
           // controller.DisplayEnrolledEmployees();
            Console.ReadKey();
        }
    }
}

Output

Here is the output.
***MVC architecture Demo***
 ***This is a console view of currently enrolled employees.***
Amit is enrolled with id : E1.
John is enrolled with id : E2.
Sam is enrolled with id : E3.
---------------------
Trying to add an employee to the registered list.The employee name is Kevin and id is E4.
Kevin is enrolled with id : E4. [added recently.]
 ***This is a console view of currently enrolled employees.***
Amit is enrolled with id : E1.
John is enrolled with id : E2.
Sam is enrolled with id : E3.
Kevin is enrolled with id : E4.
---------------------
Trying to remove an employee from the registered list.The employee id is E2.
 Employee Found.John has id: E2.
Removing this employee.
 ***This is a console view of currently enrolled employees.***
Amit is enrolled with id : E1.
Sam is enrolled with id : E3.
Kevin is enrolled with id : E4.
---------------------
Trying to remove an employee from the registered list.The employee id is E5.
At present, there is no employee with id E5.Ignoring this request.
 ***This is a console view of currently enrolled employees.***
Amit is enrolled with id : E1.
Sam is enrolled with id : E3.
Kevin is enrolled with id : E4.
---------------------
Trying to add an employee to the registered list.The employee name is Kevin and id is E4.
This employee is already added in the registered list.So, ignoring the request of addition.
 ***This is a console view of currently enrolled employees.***
Amit is enrolled with id : E1.
Sam is enrolled with id : E3.
Kevin is enrolled with id : E4.
---------------------

Q&A Session

26.1 Suppose that you have a programmer, a DBA, and a graphic designer. Can you predict their roles in an MVC architecture?

The graphic designer designs the view layer, the DBA creates the model, and the programmer works to make an intelligent controller.

26.2 What are the key advantages of using the MVC design pattern?

Some important advantages are as follows.
  • High cohesion and low coupling are the benefits of MVC. You have probably noticed that tight coupling between the model and the view is easily removed in this pattern. So, the application can be easily extendable and reusable.

  • The pattern supports parallel development.

  • You can also accommodate multiple runtime views.

26.3 What are the challenges associated with the MVC pattern?

Here are some challenges.
  • It requires highly skilled personnel.

  • For a tiny application, it may not be suitable.

  • Developers may need to be familiar with multiple languages, platforms, and technologies.

  • Multi-artifact consistency is a big concern because you are separating the overall project into three major parts.

26.4 Can you provide multiple views in this implementation?

Sure. Let’s add a new shorter view called MobileDeviceView in the application. Let’s add this class inside the View folder as follows.
using System;
using System.Collections.Generic;
using MVCPattern.Model;
namespace MVCPattern.View
{
    public class MobileDeviceView:IView
    {
        public void ShowEnrolledEmployees(List<Employee> enrolledEmployees)
        {
            Console.WriteLine(" +++This is a mobile device view of currently enrolled employees.+++ ");
            foreach (Employee emp in enrolledEmployees)
            {
                Console.WriteLine(emp.GetEmpId() + " " + emp.GetEmpName());
            }
            Console.WriteLine("+++++++++++++++++++++");
        }
    }
}
Once you add this class, your modified Solution Explorer view should be similar to Figure 26-8.
../images/463942_2_En_26_Chapter/463942_2_En_26_Fig8_HTML.jpg
Figure 26-8

Modified Solution Explorer view

Now add the following segment of code at the end of your client code(Refer the comment for your reference).
/* This segment is added to discuss a question in "Q&A Session and was
   initially commented out.Now I’m uncommenting the following three lines of code."
*/
view = new MobileDeviceView();
controller = new EmployeeController(model, view);
controller.DisplayEnrolledEmployees();

Now if you run the application, you see the modified output.

Modified Output

Here is the modified output. The last part of your output shows the effects of the new changes. The changes are shown in bold.
***MVC architecture Demo***
 ***This is a console view of currently enrolled employees.***
Amit is enrolled with id : E1.
John is enrolled with id : E2.
Sam is enrolled with id : E3.
---------------------
Trying to add an employee to the registered list.The employee name is Kevin and id is E4.
Kevin is enrolled with id : E4. [added recently.]
 ***This is a console view of currently enrolled employees.***
Amit is enrolled with id : E1.
John is enrolled with id : E2.
Sam is enrolled with id : E3.
Kevin is enrolled with id : E4.
---------------------
Trying to remove an employee from the registered list.The employee id is E2.
 Employee Found.John has id: E2.
Removing this employee.
 ***This is a console view of currently enrolled employees.***
Amit is enrolled with id : E1.
Sam is enrolled with id : E3.
Kevin is enrolled with id : E4.
---------------------
Trying to remove an employee from the registered list.The employee id is E5.
At present, there is no employee with id E5.Ignoring this request.
 ***This is a console view of currently enrolled employees.***
Amit is enrolled with id : E1.
Sam is enrolled with id : E3.
Kevin is enrolled with id : E4.
---------------------
Trying to add an employee to the registered list.The employee name is Kevin and id is E4.
This employee is already added in the registered list.So, ignoring the request of addition.
 ***This is a console view of currently enrolled employees.***
Amit is enrolled with id : E1.
Sam is enrolled with id : E3.
Kevin is enrolled with id : E4.
---------------------
 +++This is a mobile device view of currently enrolled employees .+++
E1      Amit
E3      Sam
E4      Kevin
+++++++++++++++++++++
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.38.41