Computer Scientist, Data Analyst, Software Architect
Replace Conditional With Polymorphism
January
24th,
2016
Trying to convince my team to give up on if/then/else & case…
Let’s play with some code!
First the the non Object Oriented / procedural version:
/*
* non_oop.cpp
*
* Created on: Sep 4, 2015
* Author: ferrazlealrm@ornl.gov
*
* Non OOP example of URL:
* scheme:[//domain[:port]][/]path
*/#include <iostream>
#include <string>
std::stringdomain="ornl.gov";std::stringhttp_port="80";std::stringhttps_port="443";std::stringftp_port="20";std::stringbuild_url(conststd::string&scheme,conststd::string&domain,conststd::string&port){returnscheme+"://"+domain+":"+port;}intmain(intargc,char*argv[]){if(argc!=2){std::cerr<<"Use "+std::string(argv[0])+" [http, https, ftp]"<<std::endl;exit(-1);}std::stringscheme=argv[1];if(scheme=="http"){std::cout<<build_url(scheme,domain,http_port)<<std::endl;}elseif(scheme=="https"){std::cout<<build_url(scheme,domain,https_port)<<std::endl;}elseif(scheme=="ftp"){std::cout<<build_url(scheme,domain,ftp_port)<<std::endl;}else{std::cerr<<"Scheme not valid. Use of these: http, https, ftp"<<std::endl;}return0;}
Finally the Object Oriented version:
/*
* oop.cpp
*
* Created on: Sep 4, 2015
* Author: ferrazlealrm@ornl.gov
*
* OOP example of URL:
* scheme:[//domain[:port]][/]path
*/#include <iostream>
#include <string>
#include <map>
// Abstract class: cannot be instantiated
classURL{protected:std::stringdomain="ornl.gov";std::stringport;std::stringscheme;std::stringbuild_url(conststd::string&scheme,conststd::string&port)const{returnscheme+"://"+domain+":"+port;}public:// Pure virtual function: i.e. must be overridden by a derived class
virtualstd::stringbuild_url()const=0;~URL(){};};classHttp:publicURL{public:Http(){port="80";scheme="http";}std::stringbuild_url()const{returnURL::build_url(scheme,port);}};classHttps:publicURL{public:Https(){port="443";scheme="https";}std::stringbuild_url()const{returnURL::build_url(scheme,port);}};classFtp:publicURL{public:Ftp(){port="20";scheme="ftp";}std::stringbuild_url()const{returnURL::build_url(scheme,port);}};/**
* Main function
*/std::stringbuild_url(constURL&url){returnurl.build_url();}intmain(intargc,char*argv[]){if(argc!=2){std::cerr<<"Use "+std::string(argv[0])+" [http, https, ftp]"<<std::endl;exit(-1);}std::stringscheme=argv[1];// UGLY!!!
// In real world I would have here some sort of Dependency Injection (e.g. factory)
// This just shows that we can get an object given a string
Httphttp;Httpshttps;Ftpftp;std::map<std::string,URL*>choices;choices.insert(std::make_pair("http",&http));choices.insert(std::make_pair("https",&https));choices.insert(std::make_pair("ftp",&ftp));if(scheme=="http"orscheme=="https"orscheme=="ftp"){std::cout<<build_url(*choices[scheme])<<std::endl;}else{std::cerr<<"Scheme not valid. Use of these: http, https, ftp"<<std::endl;}return0;}
I know it looks complicated, but imagine you want to add a new functionality. Let’s for example add a path to the HTTP url:
/**
* Let's imagine that I want to add a path to the URL: http://ornl.gov:80/
* I don't need to modify the code! Open/closed principle.
* Just extend it, i.e., add new code.
*/classHttpWithPath:publicHttp{protected:std::stringpath;public:HttpWithPath(std::stringpath):Http(),path(path){}std::stringbuild_url()const{std::stringdefault_url=URL::build_url(scheme,port);returndefault_url+"/"+path;}};
It does not violate the open/closed principle, which states:
“software entities should be open for extension, but closed for modification”.