{"id":1017,"date":"2019-08-11T23:30:52","date_gmt":"2019-08-11T23:30:52","guid":{"rendered":"http:\/\/tech.avant.net\/q\/?p=1017"},"modified":"2019-12-06T04:58:49","modified_gmt":"2019-12-06T04:58:49","slug":"oo-or-procedural","status":"publish","type":"post","link":"https:\/\/tech.avant.net\/q\/oo-or-procedural\/","title":{"rendered":"OOP or Procedural?"},"content":{"rendered":"\n<p>I would like to know when it is best to use object-oriented programming, and when it is best to use procedural programming.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\"><p><strong>tl;dr: neither, go with functional programming<\/strong><\/p><\/blockquote>\n\n\n\n<p>By procedural programming, I mean the kind of code you&#8217;d find <a rel=\"noreferrer noopener\" aria-label=\"programming in C (opens in a new tab)\" href=\"https:\/\/amzn.to\/2Tyxozv\" target=\"_blank\">programming in C<\/a>;  imperative control flow, functions, data structures, and algorithms. For example,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#include &lt;stdio.h>\n\nfloat f_to_c(float f) {\n    return (f - 32) * 5 \/ 9;\n}\n\nint main() {\n    float fahrenheit;\n    printf(\"Please enter the temperature in Fahrenheit: \");\n    scanf(\"%f\", &amp;fahrenheit);\n    printf(\"Temperature in Celsius = %.2f\\n\", f_to_c(fahrenheit));\n    return 0;\n}<\/pre>\n\n\n\n<p>And by object-oriented programming, I mean the kind of code with abstraction, inheritance, polymorphism, and encapsulation. For example,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import java.util.*;\n\ninterface TemperatureConverter {\n    public float convert();\n}\n\nclass Temperature {\n    float degrees;\n    Temperature(float t) {\n        degrees = t;\n    }\n}\n\nclass Fahrenheit extends Temperature implements TemperatureConverter {\n\n    Fahrenheit(float t) {\n        super(t);\n    }\n\n    public float convert() {\n        return ((degrees - 32)*5)\/9;\n    }\n\n}\n\nclass FahrenheitToCelsius {\n\n    public static void main(String[] args) {\n        Fahrenheit fahrenheit;\n        Scanner in = new Scanner(System.in);\n        System.out.print(\"Enter temperature in Fahrenheit: \");\n        fahrenheit = new Fahrenheit( in.nextFloat() );\n\n        System.out.println(\"temperature in Celsius = \" \n            + fahrenheit.convert());\n    }\n\n}<\/pre>\n\n\n\n<p>I admittedly forced some inheritance and polymorphism into the above code, but it&#8217;s arguably just as easy (if not easier) to read than the C example (despite being considerably longer).<\/p>\n\n\n\n<p>In both cases we hid the implementation details (the specific formula that converts Fahrenheit to Celsius) from the main(). However, the OOP example also hides (encapsulates) the data structure as well. In the Java example we encapsulate the float within the Temperature base class, which the Fahrenheit class inherits. And since the Fahrenheit class implements the TemperatureConverter interface, then we&#8217;re guaranteed to have a convert() method. There is still some implicit typecasting (a float to string within the println), but the idea is that the main() function doesn&#8217;t care about the underlying data structure.<\/p>\n\n\n\n<p>As <a rel=\"noreferrer noopener\" aria-label=\"Robert Martin (Uncle Bob) (opens in a new tab)\" href=\"https:\/\/amzn.to\/33s8Qwi\" target=\"_blank\">Robert Martin (Uncle Bob)<\/a> put it, &#8220;Objects expose behavior and hide data.&#8221; The Fahrenheit class exposed a convert() behavior and hid the underlying data structure. This, according to Uncle Bob, makes it easy to add new objects without changing existing behaviors. For example,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class Celsius extends Temperature implements TemperatureConverter {\n\n    Celsius(float t) {\n        super(t);\n    }\n\n    public float convert() {\n        return 9*degrees\/5 + 32;\n    }\n\n}<\/pre>\n\n\n\n<p>This code has no impact on the existing Fahrenheit class, and we can safely call convert() on both Fahrenheit and Celsius objects. Additionally, if we use generics on the Temperature class, then we could allow for different data structures (such as double or BigDecimal) on something like a Kelvin class. In OOP, adding new classes is generally easy.<\/p>\n\n\n\n<p>That said, what if we wanted to add new behavior? Maybe we want to add an isRoomTemperature() method. If so, we could add a new interface and then implement it in Celsius and Fahrenheit, but what if we had also implemented that new Kelvin class? Or several other Temperature classes? And shouldn&#8217;t the convert() method return a Temperature class? This could get messy and will lead us into <a rel=\"noreferrer noopener\" aria-label=\"DRY (opens in a new tab)\" href=\"https:\/\/en.wikipedia.org\/wiki\/Don't_repeat_yourself\" target=\"_blank\">DRY<\/a> problems. In fact, this is an area where OOP is not ideal. Even Uncle Bob admits that if we&#8217;re adding new behaviors then &#8220;we prefer data types and procedures.&#8221;<\/p>\n\n\n\n<p>This seemingly obvious and innocuous statement in <a rel=\"noreferrer noopener\" aria-label=\"Clean Code (opens in a new tab)\" href=\"https:\/\/amzn.to\/33s8Qwi\" target=\"_blank\">Clean Code<\/a> is actually very profound, especially considering the fact that OOP and classic procedural programming do not mix well in a single code-base. Whether you go with OOP or not, if Uncle Bob is correct, depends on whether or not you will be adding and managing lots of behavior, or whether you will be adding and managing lots of data types. If the behavior will be relatively unchanged, then OOP would be beneficial, but if we&#8217;re planning to add or change behavior, then procedural programming would be preferred. I honestly don&#8217;t know what kind of software projects aren&#8217;t primarily adding new behaviors (new features).<\/p>\n\n\n\n<p>For reference, adding a room temperature check is easy in the C code,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#include &lt;stdio.h>\n#include &lt;stdbool.h>\n\nbool is_c_room_temperature(float c) {\n    return c >= 20 &amp;&amp; c &lt;= 25 ? 1 : 0;\n}\n\nfloat f_to_c(float f) {\n    return (f - 32) * 5 \/ 9;\n}\n\nbool is_f_room_temperature(float f) {\n    return is_c_room_temperature(f_to_c(f));\n}\n\nint main() {\n    float fahrenheit;\n    printf(\"Please enter the temperature in Fahrenheit: \");\n    scanf(\"%f\", &amp;fahrenheit);\n    printf(\"Temperature in Celsius = %.2f\\n\", f_to_c(fahrenheit));\n    if (is_f_room_temperature(fahrenheit)) {\n        printf(\"%.2f is room temperature\\n\", fahrenheit);\n    }\n    return 0;\n}<\/pre>\n\n\n\n<p>Classic procedural code does not concern itself with adding behaviors to objects. Instead, it treats data types as data types and isolates the &#8220;procedural&#8221; behaviors into functions that are performed on those data types. If we stick to pure functions (no side effects, and all inputs map to unique outputs), then we&#8217;ll have highly testable code that can run in highly-concurrent environments.<\/p>\n\n\n\n<p>For example, adding a Kelvin conversion would look like this,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">float c_to_k(float c) {\n    return c + 273.15;\n}<\/pre>\n\n\n\n<p>Likewise, adding a Fahrenheit to Kelvin conversion would simply chain together two pure functions,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">float f_to_k(float f) {\n    return c_to_k(f_to_c(f));\n}<\/pre>\n\n\n\n<p>Procedural code focuses entirely on behavior. Adding this functionality in a pure OOP style would result a laundry list of classes, interfaces, and methods. It can get out of hand quickly, and we&#8217;d soon be researching design patterns to try to regain some sense of code quality.<\/p>\n\n\n\n<p>In practice, most developers tend to treat OOP and procedural programming with a sort of religious devotion, zealously adhering to their preferred programming style and feeling that the alternative is sacrilege. I think Uncle Bob was onto something when he said that &#8220;good software developers understand these issues without prejudice and choose the approach that is best for the job at hand.&#8221; That&#8217;s also from <a rel=\"noreferrer noopener\" aria-label=\"Clean Code (opens in a new tab)\" href=\"https:\/\/amzn.to\/2YZEcLq\" target=\"_blank\">Clean Code<\/a>, a book that should be read at least as often as it&#8217;s referenced (it&#8217;s a bit like George Orwell&#8217;s <a rel=\"noreferrer noopener\" aria-label=\"1984 (opens in a new tab)\" href=\"https:\/\/amzn.to\/2YGAtmB\" target=\"_blank\">1984<\/a>, most people reference it without ever having read it). <\/p>\n\n\n\n<p>Uncle Bob is certainly more diplomatic than Joe Armstrong, the creator of Erlang, who had famously said,<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-style-large\"><p>&#8220;The problem with object-oriented languages is they\u2019ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.&#8221;<\/p><\/blockquote>\n\n\n\n<p>To date, I&#8217;ve never heard a reasonable counter-argument to this objection to OOP, namely, that objects bind data structures and functions together (which inevitably leads to an explosion of side-effects). Even as you try to decouple the banana from the gorilla, you end up creating even more classes, more side effects, and most likely an even <a rel=\"noreferrer noopener\" aria-label=\"worse (opens in a new tab)\" href=\"https:\/\/docs.spring.io\/spring-framework\/docs\/2.5.x\/api\/org\/springframework\/aop\/framework\/AbstractSingletonProxyFactoryBean.html\" target=\"_blank\">worse<\/a> problem. I&#8217;m not sure I&#8217;d go so far as to say <a rel=\"noreferrer noopener\" aria-label=\"OO Sucks (opens in a new tab)\" href=\"http:\/\/harmful.cat-v.org\/software\/OO_programming\/why_oo_sucks\" target=\"_blank\">OO Sucks<\/a>, but I am hard pressed to defend OOP in light of <a rel=\"noreferrer noopener\" aria-label=\"decades of hard learned lessons (opens in a new tab)\" href=\"https:\/\/medium.com\/better-programming\/object-oriented-programming-the-trillion-dollar-disaster-92a4b666c7c7\" target=\"_blank\">decades of hard learned lessons<\/a>.<\/p>\n\n\n\n<p>Obviously, good code is preferable to bad code in any language. There is plenty of bad procedural code out in the world. But honestly, in OOP you often find good programmers writing bad code. Let&#8217;s go back to some of the <a rel=\"noreferrer noopener\" aria-label=\"earliest lessons (opens in a new tab)\" href=\"https:\/\/amzn.to\/2Z2x7cW\" target=\"_blank\">earliest lessons<\/a> in software engineering, specifically, Fred Brook&#8217;s essay, <a rel=\"noreferrer noopener\" aria-label=\"No Silver Bullet (opens in a new tab)\" href=\"https:\/\/amzn.to\/2Z2x7cW\" target=\"_blank\">No Silver Bullet<\/a>, and ask ourselves how much accidental complexity has been created by OOP? How much code in an average OOP project is tackling the essential complexity of a problem versus the accidental complexity?<\/p>\n\n\n\n<p>In fairness, OOP was popularized by Java, which solved many problems from the early days of C and C++ (such as garbage collection and platform independence). In the decades since, Java has added capabilities found in modern languages (such as lambda expressions, collections, stream api, higher-order functions, etc). Most of the new capabilities come from the world of functional programming, and exactly zero of these capabilities come from OOP.<\/p>\n\n\n\n<p>Whether we like it or not, the future may not be kind to OOP. Multi-core architectures and distributed computing are pushing software into high-concurrency asynchronous environments. Even worse, the push to cloud computing and microservices leads us to an increase in latency within a highly concurrent asynchronous world. This is an ideal environment for a separation of data structures from functions (pure functions). This is a great environment for Haskell and Erlang (or coding pure functions using Scala, Python, or Go), but regardless of the language, you couldn&#8217;t ask for a worse environment for OOP.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I would like to know when it is best to use object-oriented programming, and when it is best to use procedural programming. tl;dr: neither, go with functional programming By procedural programming, I mean the kind of code you&#8217;d find programming in C; imperative control flow, functions, data structures, and algorithms. For example, And by object-oriented [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[20,10,22],"tags":[],"_links":{"self":[{"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/posts\/1017"}],"collection":[{"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/comments?post=1017"}],"version-history":[{"count":10,"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/posts\/1017\/revisions"}],"predecessor-version":[{"id":1040,"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/posts\/1017\/revisions\/1040"}],"wp:attachment":[{"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/media?parent=1017"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/categories?post=1017"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tech.avant.net\/q\/wp-json\/wp\/v2\/tags?post=1017"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}