{"id":10,"date":"2016-12-10T19:07:46","date_gmt":"2016-12-10T19:07:46","guid":{"rendered":"https:\/\/blog.kineteque.com\/?p=10"},"modified":"2016-12-10T19:07:46","modified_gmt":"2016-12-10T19:07:46","slug":"classes-are-better-than-maps","status":"publish","type":"post","link":"https:\/\/blog.kineteque.com\/?p=10","title":{"rendered":"Map as Class Anti-Pattern"},"content":{"rendered":"<p>Today, I will write about a Map anti-pattern that I have sometimes observed.<\/p>\n<p>I have come across code as follows from time to time:<\/p>\n<pre class=\"lang:java decode:true\">Map&lt;String, Object&gt; json = new HashMap&lt;&gt;(); \u00a0 \/\/bad don't do this!!!\n\n\/\/ or\n\nMap&lt;String, Object&gt; parameters = new HashMap&lt;&gt;();  \/\/again don't do this<\/pre>\n<p>I suggested to\u00a0the developer who wrote similar code to use a class instead of a Map. \u00a0I explained\u00a0that a Map should be used for mapping problems, such as associating data with key value pairs.<\/p>\n<p>The developer was upset, and argued back that\u00a0using a class instead of a map is\u00a0over-engineering. I interpreted this as\u00a0some sort of logical fallacy &#8220;Ad Hominem&#8221; attack against classes. I exclaimed &#8220;Using a map is under-engineering!\u00a0At the very least, we can introduce an interface\u00a0that functions\u00a0a marker class, and let Jackson perform the transformations?&#8221; This developer would not budge, and stated that she did not want to write a class for everything that will become serialized as JSON. I stepped back, gave her the benefit of the doubt, looked further at the code, noted the pervasiveness of this anti-pattern, and now I am writing this.<\/p>\n<p><!--more--><\/p>\n<p>Deeper analysis of\u00a0the code affirmed my conviction that classes should not be substituted with maps. Now I will attempt to objectively make my case.<\/p>\n<p>My most compelling reason is that it is not cohesive. \u00a0When maps are created, fields are assigned at different places within the code, and ultimately are consumed at different sections of the code. This production and consumption of the data loses its proximity from the location of its original declaration. It would often happen in a different method, a different class, or even a different library. This forces the consumer to search through multiple modules, methods, or classes in order to discover which data resides within the map. This a time-consuming and error prone process that takes much more time than just creating a plain ordinary Java object. Then after time is spent on this lookup, the following problems still exist:<\/p>\n<ul>\n<li>A map promotes magic &#8220;Strings&#8221; for the keys unless a class is written just to\u00a0contain the keys. If there is no class written for the keys, then either a) somebody must read the source code to determine which key value pairs and types are\u00a0in the map b) the application must be debugged to determine which key value pairs and types are in the map or c) information can pass through the organization through\u00a0tribal knowledge.<\/li>\n<li>There can be a typo in the name of the field, which is not a compilation, but instead a runtime error or bug.<\/li>\n<li>Refactoring has to change String names and types, and there is less tooling available for that.<\/li>\n<li>Maps cannot have any business methods.<\/li>\n<li>Maps cannot support primitives.<\/li>\n<li>Composition with other classes requires more and more hash maps.<\/li>\n<li>Maps do not work well with other frameworks that use annotations such as the Validations framework (JSR 349), JPA, Spring, Juice, etc.<\/li>\n<li>Maps performs more slowly than a class, and also requires more memory.<\/li>\n<li>Extracting values from a map requires\u00a0a cast, which pollutes the readability\u00a0of the code.<\/li>\n<li>The lack of strong typing of a value in the Map makes it error prone.<\/li>\n<\/ul>\n<h2>Conclusion<\/h2>\n<p>If you need serialize data\u00a0then introduce a class that represents that data and then use a serialization framework that can perform the serialization for you.<\/p>\n<pre class=\"lang:java decode:true\">ObjectMapper mapper = new ObjectMapper();\nString json = mapper.writeValueAsString(anyObjectWithGetters);<\/pre>\n<p>If you need to read JSON into a general purpose data-structure (situations where you don&#8217;t know what class you are parsing, then use Jackson&#8217;s JsonNode class.<\/p>\n<p>If you need to pass data to other classes or methods, then again, introduce a class. You&#8217;ll find the code much more cohesive, readable\u00a0and safe.<\/p>\n<p>If you are tired of writing complete classes then use <a href=\"https:\/\/projectlombok.org\">Lombok.<\/a>\u00a0With Lombok you can write POJOs as simply as this:<\/p>\n<pre class=\"lang:java decode:true\">import lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\nimport lombok.Setter;\nimport lombok.ToString;\n\nimport java.time.Instant;\n\n@Getter\n@Setter\n@ToString\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Profile {\n   private String firstName;\n   private String lastName;\n   private Instant dob;\n}<\/pre>\n<p>Or use the new JDK 14 record type.<\/p>\n<pre class=\"lang:java decode:true \">public record Person (String firstName, String lastName, Instant dob) {}<\/pre>\n<p>It is not over-engineering to introduce a class.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today, I will write about a Map anti-pattern that I have sometimes observed. I have come across code as follows from time to time: Map&lt;String, Object&gt; json = new HashMap&lt;&gt;(); \u00a0 \/\/bad don&#8217;t do this!!! \/\/ or Map&lt;String, Object&gt; parameters = new HashMap&lt;&gt;(); \/\/again don&#8217;t do this I suggested to\u00a0the developer who wrote similar code [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[15],"class_list":["post-10","post","type-post","status-publish","format-standard","hentry","category-effective-java","tag-java"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blog.kineteque.com\/index.php?rest_route=\/wp\/v2\/posts\/10","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.kineteque.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.kineteque.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.kineteque.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.kineteque.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10"}],"version-history":[{"count":0,"href":"https:\/\/blog.kineteque.com\/index.php?rest_route=\/wp\/v2\/posts\/10\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.kineteque.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.kineteque.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=10"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.kineteque.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=10"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}