CSC/ECE 517 Fall 2009/wiki2 4 railroad: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(15 intermediate revisions by the same user not shown)
Line 1: Line 1:
Student: Manhattan
==Introduction==
==Introduction==


[http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming Polymorphism] is one of the fundamental strengths of object oriented systems and design.  Polymorphism can not only make code more closely mirror the “real” world, it can also help make code more elegant and maintainable.  One such was is by reducing conditional statements, such as “if” statements or “switch” statements.  “Replacing Conditional with Polymorphism” is a commonly accepted refactoring technique.  This article explores several examples of why replacing conditionals with polymorphism is desirable.  
[http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming Polymorphism] is one of the fundamental strengths of [http://en.wikipedia.org/wiki/Object-oriented_programming object oriented] systems and design.  Polymorphism can not only make code more closely mirror the “real” world, it can also help make code more elegant and maintainable.  One way polymorphism can help make code more elegant and maintainable is by reducing the number of conditional statements.  [http://en.wikipedia.org/wiki/Conditional_%28programming%29 Conditional statements] are statements in a programming language that perform different actions based on the current state of a variable or object.  Examples of conditional statements include the “if” statement and the “switch” statementThis concept of [http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html “Replacing Conditional with Polymorphism”] is a commonly accepted [http://en.wikipedia.org/wiki/Code_refactoring refactoring] technique.  This article explores several examples of why replacing conditional statements with polymorphism is desirable.  


==When Conditional Statements Are Useful==
==When Conditional Statements Are Useful==
In large software systems, conditional statements can make code less modular and require that more lines of code be updated when a change needs to be made. However, when are they useful?
In large software systems, conditional statements can make code less modular and require that more lines of code be updated when a change needs to be made. However, before beginning our discussion of why conditional statements are bad, let's look at a simple example of when they are useful and appropriate.


Let’s start by considering a simple example.  Let’s say we have a system that allows for management of train stations.  Perhaps, a snippet of code from the system for selecting a station might look like:
Let’s say we have a system that allows for management of train stations.  Perhaps, a snippet of code from the system for selecting a station might look like:


  if(users_selection == “PHL”)
  if(users_selection == “PHL”)
  {
  {
        /* Station = Philadelphia, PA */
        /* Station = Philadelphia, PA */
  }
  }
  else if(users_selection == “NYC”)
  else if(users_selection == “NYC”)
Line 24: Line 26:
  /* Station = Raleigh, NC */
  /* Station = Raleigh, NC */
  }
  }
 
  ... and so on ...
  ... and so on ...


This code works well.  Even when a new station needs to be added, the programmer can simply add another “else if” statement to make the code work.  Simple conditional statements, such as the example above, are arguably necessary in every non-trivial program.  When encapsulated within a classes method to perform simple comparisons, conditionals provide a very simple and elegant way to add functionality to the system.  
This code works well.  Even when a new station needs to be added, the programmer can simply add another “else if” statement to make the code work.  Simple conditional statements, such as the example above, are arguably necessary in every non-trivial program.  When encapsulated within a class's method to perform simple comparisons, conditionals provide a very simple and elegant way to add functionality to the system.  


==Conditionals that Make it Hard to Maintain Code==
==Conditionals that Make it Hard to Maintain Code==
Arguably, any non-trivial software system will contain some form of complex logic.  Let’s say our system also contains a way to get the services the station offers; these services might be: food for travelers, train fuel, connections to local subway trains, etc.  If we tried to use conditional statements to do this, we might see something like:
Arguably, any non-trivial software system will contain some form of complex logic.  Let’s say our system also contains a way to get the services the station offers; these services might be: food for travelers, train fuel, connections to local subway trains, etc.  If we tried to use conditional statements to do this, we might see something like:


switch( my_station.get_name() )
switch( my_station.get_name() )
{
{
case PHL:
case PHL:
/* Return things about Philadelphia’s train station */
/* Return things about Philadelphia’s train station */
break;
break;
 
case NYC:
case NYC:
/* Return things about New York’s train station */
/* Return things about New York’s train station */
break;
break;
 
case RDU:
case RDU:
/* Return things about Raleigh’s station */
/* Return things about Raleigh’s station */
break;
break;
 
case PVD:
case PVD:
/* Return things about Providence’s station */
/* Return things about Providence’s station */
break;
break;
 
... and so on ...
... and so on ...
 
}
}


This approach will work, but, what if each station has many different types of services?  Or, what if certain services are not available at certain stations.  That is, what if each station has services, but they are mostly unique to that station.  We could probably get around this by creating, and returning, an instance of an STATION_INFO class.  However, this code is arguably not very elegant.  In one switch statement, there could be many different things happening and it could be very difficult to maintain.   
This approach will work, but, what if each station has many different types of services?  Or, what if certain services are not available at certain stations.  That is, what if each station has services, but they are mostly unique to that station.  We could probably get around this by creating, and returning, an instance of an STATION_INFO class.  However, this code is arguably not very elegant.  In one switch statement, there could be many different things happening and it could be very difficult to maintain.   
Line 69: Line 71:
Assuming each subclass is required by the TRAIN_STATION superclass to implement a method called ‘get_my_services’, the switch statement in the previously example would simply become:
Assuming each subclass is required by the TRAIN_STATION superclass to implement a method called ‘get_my_services’, the switch statement in the previously example would simply become:


/* Get Philly’s train station services */
/* Get Philly’s train station services */
services = the_airports[0]. get_my_services;
services = the_airports[0]. get_my_services;
 
/* Get New York’s train station services */
/* Get New York’s train station services */
services = the_airports[1]. get_my_services;
services = the_airports[1]. get_my_services;
 
/* Get Raleigh's train station services */
/* Get Raleigh's train station services */
services = the_airports[2]. get_my_services;
services = the_airports[2]. get_my_services;
 
/* Get Providence’s train station services */
/* Get Providence’s train station services */
services = the_airports[3]. get_my_services;
services = the_airports[3]. get_my_services;
 
... and so on ...  
... and so on ...  


Now, if any of the services need to be modified, updated, or otherwise changed, we can simply go to the specific train station subclass and modify its ‘ get_my_services’ method.  The code is very easy to understand.
Now, if any of the services need to be modified, updated, or otherwise changed, we can simply go to the specific train station subclass and modify its ‘ get_my_services’ method.  The code is very easy to understand.


==Conditional Statements Security Risk==
==Conditional Statements Security Risk==
Conditional statements can also pose a significant security risk that can be mitigated using polymorphism.  Keeping with our train station management system example, let’s assume we have functionally to return a string that indicates the name of the local public transit system that connects to the train station.  Again, if we used a conditional, such as an “switch” statement we may have:
Conditional statements can also pose a significant security risk that can be mitigated using polymorphism.  Keeping with our train station management system example, let’s assume we have functionally to return a string that indicates the name of the local public transit system that connects to the train station.  Again, if we used a conditional, such as a “switch” statement we may have:


  switch( my_station.get_transit_service_string() )
  switch( my_station.get_transit_service_string() )
Line 102: Line 104:
Notice that Raleigh and Providence were both left off.  Why?  Perhaps these cities have public transit systems, but they do not connect to the train station.  Perhaps the programmer forgot to put them in.  There is an infinite number of reasons why they may have been left off.  With a standard conditional statement, there is virtually no way to enforce that all conditions are enumerated.  If something such as an uninitiated string or other variable is introduced, it could cause problems in other parts of the system.  Of course, in all major languages, a ‘switch’ statement has a ‘default’ case that can be used as a catch-all.
Notice that Raleigh and Providence were both left off.  Why?  Perhaps these cities have public transit systems, but they do not connect to the train station.  Perhaps the programmer forgot to put them in.  There is an infinite number of reasons why they may have been left off.  With a standard conditional statement, there is virtually no way to enforce that all conditions are enumerated.  If something such as an uninitiated string or other variable is introduced, it could cause problems in other parts of the system.  Of course, in all major languages, a ‘switch’ statement has a ‘default’ case that can be used as a catch-all.


When using polymorphism, this problem can be easily avoided.  Java, C++, and Ruby all contain ways to enforce that subclasses implement certain methods or at least allow the base class to “safely” implement the method.  In Java, a class’s methods can be declared as ‘abstract’, in C++ they can be declared as virtual, and in Ruby, the ‘method_missing’ method can be used.
When using polymorphism, this problem can be easily avoided.  Java, C++, and Ruby all contain ways to enforce that subclasses implement certain methods or at least allow the base class to “safely” implement the method.  For example, in Java, a class’s methods can be declared as ‘abstract’, in C++ they can be declared as virtual, and in Ruby, the ‘method_missing’ method can be added to a class to enforce that subclasses implement certain methods.


For example, let implement the connecting mass transit string with an abstract method in Java.
For example, let's implement the connecting mass transit string with an abstract method in Java.


  /* The Base Class */
  /* The Base Class */
Line 125: Line 127:
http://sourcemaking.com/refactoring/replace-conditional-with-polymorphism
http://sourcemaking.com/refactoring/replace-conditional-with-polymorphism


[3] Information on ‘abstract’ classes and methods in Java:
[3] Information on "abstract" classes and methods in Java:
http://java.sun.com/docs/books/tutorial/java/IandI/abstract.html
http://java.sun.com/docs/books/tutorial/java/IandI/abstract.html


Line 131: Line 133:
http://www.rubycentral.com/pickaxe/ref_c_object.html
http://www.rubycentral.com/pickaxe/ref_c_object.html


[5] Information in Virtual methods in several languages:
[5] Information on Virtual methods in several languages:
http://en.wikipedia.org/wiki/Virtual_function
http://en.wikipedia.org/wiki/Virtual_function

Latest revision as of 02:10, 15 October 2009

Student: Manhattan

Introduction

Polymorphism is one of the fundamental strengths of object oriented systems and design. Polymorphism can not only make code more closely mirror the “real” world, it can also help make code more elegant and maintainable. One way polymorphism can help make code more elegant and maintainable is by reducing the number of conditional statements. Conditional statements are statements in a programming language that perform different actions based on the current state of a variable or object. Examples of conditional statements include the “if” statement and the “switch” statement. This concept of “Replacing Conditional with Polymorphism” is a commonly accepted refactoring technique. This article explores several examples of why replacing conditional statements with polymorphism is desirable.

When Conditional Statements Are Useful

In large software systems, conditional statements can make code less modular and require that more lines of code be updated when a change needs to be made. However, before beginning our discussion of why conditional statements are bad, let's look at a simple example of when they are useful and appropriate.

Let’s say we have a system that allows for management of train stations. Perhaps, a snippet of code from the system for selecting a station might look like:

if(users_selection == “PHL”)
{
        /* Station = Philadelphia, PA */
}
else if(users_selection == “NYC”)
{
	/* Station = New York, NY */
}
else if(user_selection == “PVD”)
{ 
	/* Station = Providence, RI */
}
else if(user_selection == “RDU”)
{
	/* Station = Raleigh, NC */
}

... and so on ...

This code works well. Even when a new station needs to be added, the programmer can simply add another “else if” statement to make the code work. Simple conditional statements, such as the example above, are arguably necessary in every non-trivial program. When encapsulated within a class's method to perform simple comparisons, conditionals provide a very simple and elegant way to add functionality to the system.

Conditionals that Make it Hard to Maintain Code

Arguably, any non-trivial software system will contain some form of complex logic. Let’s say our system also contains a way to get the services the station offers; these services might be: food for travelers, train fuel, connections to local subway trains, etc. If we tried to use conditional statements to do this, we might see something like:

switch( my_station.get_name() )
{
	case PHL:
		/* Return things about Philadelphia’s train station */
	break;

	case NYC:
		/* Return things about New York’s train station */
	break;

	case RDU:
		/* Return things about Raleigh’s station */
	break;

	case PVD:
		/* Return things about Providence’s station */
	break;

... and so on ...

}

This approach will work, but, what if each station has many different types of services? Or, what if certain services are not available at certain stations. That is, what if each station has services, but they are mostly unique to that station. We could probably get around this by creating, and returning, an instance of an STATION_INFO class. However, this code is arguably not very elegant. In one switch statement, there could be many different things happening and it could be very difficult to maintain.

But what if we created a train station base class, then created subclasses that represent each individual train station? We could use polymorphism to always return the correct station information. For example,

TRAIN_STATION[ ] the_stations = new TRAIN_STATION[10];

the_stations[0] = new PHL_STATION;
the_stations[1] = new NYC_STATION;
the_stations[2] = new RDU_STATION;
the_stations[3] = new PVD_STATION;

... and so on ...

Assuming each subclass is required by the TRAIN_STATION superclass to implement a method called ‘get_my_services’, the switch statement in the previously example would simply become:

/* Get Philly’s train station services */
services = the_airports[0]. get_my_services;

/* Get New York’s train station services */
services = the_airports[1]. get_my_services;

/* Get Raleigh's train station services */
services = the_airports[2]. get_my_services;

/* Get Providence’s train station services */
services = the_airports[3]. get_my_services;

... and so on ... 

Now, if any of the services need to be modified, updated, or otherwise changed, we can simply go to the specific train station subclass and modify its ‘ get_my_services’ method. The code is very easy to understand.

Conditional Statements Security Risk

Conditional statements can also pose a significant security risk that can be mitigated using polymorphism. Keeping with our train station management system example, let’s assume we have functionally to return a string that indicates the name of the local public transit system that connects to the train station. Again, if we used a conditional, such as a “switch” statement we may have:

switch( my_station.get_transit_service_string() )
{
	case PHL:
		public_transit_name = “SEPTA”;
	break;

	case NYC:
		public_transit_name = “MTA”;
	break;

...and so on ...

Notice that Raleigh and Providence were both left off. Why? Perhaps these cities have public transit systems, but they do not connect to the train station. Perhaps the programmer forgot to put them in. There is an infinite number of reasons why they may have been left off. With a standard conditional statement, there is virtually no way to enforce that all conditions are enumerated. If something such as an uninitiated string or other variable is introduced, it could cause problems in other parts of the system. Of course, in all major languages, a ‘switch’ statement has a ‘default’ case that can be used as a catch-all.

When using polymorphism, this problem can be easily avoided. Java, C++, and Ruby all contain ways to enforce that subclasses implement certain methods or at least allow the base class to “safely” implement the method. For example, in Java, a class’s methods can be declared as ‘abstract’, in C++ they can be declared as virtual, and in Ruby, the ‘method_missing’ method can be added to a class to enforce that subclasses implement certain methods.

For example, let's implement the connecting mass transit string with an abstract method in Java.

/* The Base Class */
class TRAIN_STATION
{
	abstract string get_transit_service_string(void);
	
	... other methods ...
}

By declaring the ‘get_transit_service_string’ method as ‘abstract’, we force any classes that inherit from TRAIN_STATION to implement the ‘get_transit_service_string’ method. If not, a compiler error will be generated.

External Resources

The following are a list of external resources that contain more information and examples on polymorphism vs. conditional statements.

[1] An example of an Employee class that calculates salary: http://www.eli.sdsu.edu/courses/spring01/cs635/notes/refactorPatterns/refactorPatterns.html#Heading3

[2] Several Examples of refactoring code to replace conditionals with polymorphism: http://sourcemaking.com/refactoring/replace-conditional-with-polymorphism

[3] Information on "abstract" classes and methods in Java: http://java.sun.com/docs/books/tutorial/java/IandI/abstract.html

[4] Information on Ruby’s ‘method_missing’ method: http://www.rubycentral.com/pickaxe/ref_c_object.html

[5] Information on Virtual methods in several languages: http://en.wikipedia.org/wiki/Virtual_function