Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Separation of concerns; MVC; why?

I'm currently reading up on OO before I embark upon my next major project. To give you some quick background, I'm a PHP developer, working on web applications.

One area that particularly interests me is the User Interface; specifically how to build this and connect it to my OO "model".

I've been doing some reading on this area. One of my favourites is this: Building user interfaces for object-oriented systems

"All objects must provide their own UI"

Thinking about my problem, I can see this working well. I build my "user" object to represent someone who has logged into my website, for example. One of my methods is then "display_yourself" or similar. I can use this throughout my code. Perhaps to start with this will just be their name. Later, if I need to adjust to show their name+small avatar, I can just update this one method and hey-presto, my app is updated. Or if I need to make their name a link to their profile, hey-presto I can update again easily from one place.

In terms of an OO system; I think this approach works well. Looking on other StackOverflow threads, I found this under "Separation of Concerns": Soc

"In computer science, separation of concerns (SoC) is the process of breaking a computer program into distinct features that overlap in functionality as little as possible. A concern is any piece of interest or focus in a program. Typically, concerns are synonymous with features or behaviors. Progress towards SoC is traditionally achieved through modularity and encapsulation, with the help of information hiding."

To my mind I have achieved this. My user object hides all it's information. I don't have any places in my code where I say $user->get_user_name() before I display it.

However, this seems to go against what other people seem to think of as "best practice".

To quote the "selected" (green one) answer from the same question:

"The separation of concerns is keeping the code for each of these concerns separate. Changing the interface should not require changing the business logic code, and vice versa. Model-View-Controller (MVC) design pattern is an excellent example of separating these concerns for better software maintainability."

Why does this make for better software maintainability? Surely with MVC, my View has to know an awful lot about the Model? Read the JavaWorld article for a detailed discussion on this point: Building user interfaces for object-oriented systems

Anyway... getting to the actual point, finally!

1. Can anyone recommend any books that discuss this in detail? I don't want an MVC book; I'm not sold on MVC. I want a book that discusses OO / UI, the potential issues, potential solutuions etc.. (maybe including MVC) Arthur Riel's Object-Oriented Design Heuristics

touches on it (and is an excellent book as well!), but I want something that goes into more detail.

2. Can anyone put forward an argument that is as well-explained as Allen Holub's JavaWorld article that explains why MVC is a good idea?

Many thanks for anyone who can help me reach a conclusion on this.

like image 534
Dave Avatar asked Mar 12 '09 17:03

Dave


People also ask

Why separation of concerns is important?

At its essence, Separation of concerns is about order. The overall goal of separation of concerns is to establish a well-organized system where each part fulfills a meaningful and intuitive role while maximizing its ability to adapt to change.

Why is it important to separate model from view?

The reason for separation of Models, Views, and Controllers revolves around the idea that most development for enterprise applications is done by a team of developers. Imagine 10 developers trying to work on your controller. But all they want to do is add something to the model.

What is the principle of separation of concerns in C#?

Separation of concerns This principle asserts that software should be separated based on the kinds of work it performs. For instance, consider an application that includes logic for identifying noteworthy items to display to the user, and which formats such items in a particular way to make them more noticeable.

What is the separation of concerns in asp net?

What is Separation of Concern? Wikipedia says “In computer science, separation of concerns (SoC) is a design principle for separating a computer program into distinct sections, such that each section addresses a separate concern. A concern is a set of information that affects the code of a computer program.”


2 Answers

This is a failure in how OOP is often taught, using examples like rectangle.draw() and dinosaur.show() that make absolutely no sense.

You're almost answering your own question when you talk about having a user class that displays itself.

"Later, if I need to adjust to show their name+small avatar, I can just update this one method and hey-presto, my app is updated."

Think about just that little piece for moment. Now take a look at Stack Overflow and notice all of the places that your username appears. Does it look the same in each case? No, at the top you've just got an envelope next to your username followed by your reputation and badges. In a question thread you've got your avatar followed by your username with your reputation and badges below it. Do you think that there is a user object with methods like getUserNameWithAvatarInFrontOfItAndReputationAndBadgesUnderneath() ? Nah.

An object is concerned with the data it represents and methods that act on that data. Your user object will probably have firstName and lastName members, and the necessary getters to retrieve those pieces. It might also have a convenience method like toString() (in Java terms) that would return the user's name in a common format, like the first name followed by a space and then the last name. Aside from that, the user object shouldn't do much else. It is up to the client to decide what it wants to do with the object.

Take the example that you've given us with the user object, and then think about how you would do the following if you built a "UI" into it:

  1. Create a CSV export showing all users, ordered by last name. E.g. Lastname, Firstname.
  2. Provide both a heavyweight GUI and a Web-based interface to work with the user object.
  3. Show an avatar next to the username in one place, but only show the username in another.
  4. Provide an RSS list of users.
  5. Show the username bold in one place, italicized in another, and as a hyperlink in yet another place.
  6. Show the user's middle initial where appropriate.

If you think about these requirements, they all boil down to providing a user object that is only concerned with the data that it should be concerned with. It shouldn't try to be all things to everyone, it should just provide a means to get at user data. It is up to each of the many views you will create to decide how it wants to display the user data.

Your idea about updating code in one place to update your views in many places is a good one. This is still possible without mucking with things at a too low of a level. You could certainly create widget-like classes that would encapsulate your various common views of "stuff", and use them throughout your view code.

like image 119
Boden Avatar answered Sep 24 '22 05:09

Boden


Here's the approach I take when creating websites in PHP using an MVC/separation of concerns pattern:

The framework I use has three basic pieces:

  • Models - PHP Classes. I add methods to them to fetch and save data. Each model represents a distinct type of entity in the system: users, pages, blog posts
  • Views - Smarty templates. This is where the html lives.
  • Controllers - PHP classes. These are the brains of the application. Typically urls in the site invoke methods of the class. example.com/user/show/1 would invoke the $user_controller->show(1) method. The controller fetches data out of the model and gives it to the view.

Each of these pieces has a specific job or "concern". The model's job is to provide a clean interface to the data. Typically the site's data is stored in a SQL database. I add methods to the model for fetching data out and saving data in.

The view's job is to display data. All HTML markup goes in the view. Logic to handle zebra-striping for a table of data goes in the view. Code to handle the format that a date should be displayed in goes in the view. I like using Smarty templates for views because it offers some nice features to handle things like that.

The controller's job is to act as an intermediary between the user, the model, and the view.

Let's look at an example of how these come together and where the benefits lie:

Imagine a simple blog site. The main piece of data is a post. Also, imagine that the site keeps track of the number of times a post is viewed. We'll create a SQL table for that:

posts id date_created title body hits 

Now suppose you would like to show the 5 most popular posts. Here's what you might see in a non MVC application:

$sql = "SELECT * FROM posts ORDER BY hits DESC LIMIT 5"; $result = mysql_query($sql);  while ($row = mysql_fetch_assoc($result)) {     echo "<a href="post.php?id=$row['id']">$row['title']</a><br />"; } 

This snippet is pretty straightforward and works well if:

  1. It is the only place you want to show the most popular posts
  2. You never want to change how it looks
  3. You never decide to change what a "popular post" is

Imagine that you want to show the 10 most popular posts on the home page and the 5 most popular in a sidebar on subpages. You now need to either duplicate the code above, or put it in an include file with logic to check where it is being displayed.

What if you want to update the markup for the home page to add a "new-post" class to posts that were created today?

Suppose you decide that a post is popular because it has a lot of comments, not hits. The database will change to reflect this. Now, every place in your application that shows popular posts must be updated to reflect the new logic.

You are starting to see a snowball of complexity form. It's easy to see how things can become increasingly difficult to maintain over the course of a project. Also, consider the complexity when multiple developers are working on a project. Should the designer have to consult with the database developer when adding a class to the output?

Taking an MVC approach and enforcing a separation of concerns within your application can mitigate these issues. Ideally we want to separate it out into three areas:

  1. data logic
  2. application logic
  3. and display logic

Let's see how to do this:

We'll start with the model. We'll have a $post_model class and give it a method called get_popular(). This method will return an array of posts. Additionally we'll give it a parameter to specify the number of posts to return:

post_model.php  class post_model {     public function get_popular($number) {         $sql = "SELECT * FROM posts ORDER BY hits DESC LIMIT $number";         $result = mysql_query($sql);         while($row = mysql_fetch_assoc($result)) {             $array[] = $row;         }         return $array;     }  } 

Now for the homepage we have a controller, we'll call it "home". Let's imagine that we have a url routing scheme that invokes our controller when the home page is requested. It's job is to get the popular posts and give them to the correct view:

home_controller.php  class home_controller {     $post_model = new post_model();     $popular_posts = $post_model->get_popular(10);      // This is the smarty syntax for assigning data and displaying     // a template. The important concept is that we are handing over the      // array of popular posts to a template file which will use them      // to generate an html page     $smarty->assign('posts', $popular_posts);     $smarty->view('homepage.tpl'); } 

Now let's see what the view would look like:

homepage.tpl     {include file="header.tpl"}   // This loops through the posts we assigned in the controller  {foreach from='posts' item='post'}      <a href="post.php?id={$post.id}">{$post.title}</a>  {/foreach}  {include file="footer.tpl"} 

Now we have the basic pieces of our application and can see the separation of concerns.

The model is concerned with getting the data. It knows about the database, it knows about SQL queries and LIMIT statements. It knows that it should hand back a nice array.

The controller knows about the user's request, that they are looking at the homepage. It knows that the homepage should show 10 popular posts. It gets the data from the model and gives it to the view.

The view knows that an array of posts should be displayed as a series of achor tags with break tags after them. It knows that a post has a title and an id. It knows that a post's title should be used for the anchor text and that the posts id should be used in the href. The view also knows that there should be a header and footer shown on the page.

It's also important to mention what each piece doesn't know.

The model doesn't know that the popular posts are shown on the homepage.

The controller and the view don't know that posts are stored in a SQL database.

The controller and the model don't know that every link to a post on the homepage should have a break tag after it.

So, in this state we have established a clear separation of concerns between data logic (the model), application logic (the controller), and display logic (the view). So now what? We took a short simple PHP snippet and broke it into three confusing files. What does this give us?

Let's look at how having a separation of concerns can help us with the issues mentioned above. To reiterate, we want to:

  1. Show popular posts in a sidebar on subpages
  2. Highlight new posts with an additional css class
  3. Change the underlying definition of a "popular post"

To show the popular posts in a sidebar we'll add two files our subpage:

A subpage controller...

subpage_controller.php  class subpage_controller {     $post_model = new post_model();     $popular_posts = $post_model->get_popular(5);      $smarty->assign('posts', $popular_posts);     $smarty->view('subpage.tpl'); } 

...and a subpage template:

subpage.tpl  {include file="header.tpl"}  <div id="sidebar">   {foreach from='posts' item='post'}     <a href="post.php?id={$post.id}">{$post.title}</a>  {/foreach}  </div>  {include file="footer.tpl"} 

The new subpage controller knows that the subpage should only show 5 popular posts. The subpage view knows that subpages should put the list of posts inside a sidebar div.

Now, on the homepage we want to highlight new posts. We can achieve this by modifying the homepage.tpl.

{include file="header.tpl"}   {foreach from='posts' item='post'}     {if $post.date_created == $smarty.now}         <a class="new-post" href="post.php?id={$post.id}">{$post.title}</a>     {else}         <a href="post.php?id={$post.id}">{$post.title}</a>     {/if}  {/foreach}  {include file="footer.tpl"} 

Here the view handles all of the new logic for displaying popular posts. The controller and the model didn't need to know anything about that change. It's purely display logic. The subpage list continues to show up as it did before.

Finally, we'd like to change what a popular post is. Instead of being based on the number of hits a page got, we'd like it to be based on the number of comments a post got. We can apply that change to the model:

post_model.php  class post_model {     public function get_popular($number) {         $sql = "SELECT * , COUNT(comments.id) as comment_count                 FROM posts                  INNER JOIN comments ON comments.post_id = posts.id                 ORDER BY comment_count DESC                  LIMIT $number";         $result = mysql_query($sql);         while($row = mysql_fetch_assoc($result)) {             $array[] = $row;         }         return $array;     }  } 

We have increased the complexity of the "popular post" logic. However, once we've made this change in the model, in one place, the new logic is applied everywhere. The homepage and the subpage, with no other modifications, will now display popular posts based on comments. Our designer didn't need to be involved in this. The markup is not affected.

Hopefully, this provides a compelling example of how separating the concerns of data logic, application logic, and display logic, can make developing your application easier. Changes in one area tend to have less of an impact on other areas.

Following this convention isn't a magic bullet that will automatically make your code perfect. And you will undoubtedly come across issues where it is far less clear where the separation should be. In the end, it's all about managing complexity within the application.

You should give plenty of thought to how you construct your models. What sort of interfaces will they provide (see Gregory's answer regarding contracts)? What data format does the controller and view expect to work with? Thinking about these things ahead of time will make things easier down the road.

Also, there can be some overhead when starting a project to get all of these pieces working together nicely. There are many frameworks that provide the building blocks for models, controllers, templating engines, url routing, and more. See many other posts on SO for suggestions on PHP MVC frameworks. These frameworks will get you up and running but you as the developer are in charge of managing complexity and enforcing a separation of concerns.

I will also note that the code snippets above are just simplified examples. They may (most likely) have bugs. However, they are very similar in structure to the code I use in my own projects.

like image 40
GloryFish Avatar answered Sep 24 '22 05:09

GloryFish