Avoiding getters / seters in classes with many instance variables

I will try to cut it.

I have classes with many instance variables (30+) and therefore with many getters / setters. The classes themselves are simple, but because of the getters / setters, the LOC just exploded (and there was too much code duplicity).

So, I deleted the attributes and saved them on the map, for example

public class MyTechnicalToolClassX { //...constructor private Map<String, Object> data; public Object getAttributeByKey(AttributeKey key) { // ...doStuff, check data associated with key, etc // if (predicate == true) return otherData; return data.get(key.toString()); } public void setAttributeByKey(AttributeKey key, Object value) throws IllegalArgumentException { if(key.getType().isInstance(value)) { data.put(key.toString(), value); } else { throw IllegalArgumentException("The passed value has the wrong type, we expect: " + key.getType().getName()); } } public enum AttributeKey { someKey1("key1", String.class), someKey2("key2", Date.class), //... someKeyN("keyN", SomeType.class); private String key; private Class valueType; AttributeKey(String key, Class valueType) { this.key = key; this.valueType = valueType; } @Override public String toString() { return key; } public Class getType() { return valueType; } } // AttributeKey } // MyTechnicalToolClassX 

AttributeKey was just a string, but that way I can provide type safety in the setter. Now, my question is, I removed the duplicity of code inside the class, but I have other classes that also have many attributes (because they are some technical objects ...), what is the best approach here? To give each class its own enumeration attribute?

Decision

I added a few more thoughts. I have a security type now at compile time. Here is my new interface for getter and setter.

 public <Type, SetVal extends Type> void setAttributeByName(IAttribute<Key, Type> attribute, SetVal value); public <Type> Type getAttributeByName(IAttribute<Key, Type> attribute); 

Joshua Bloch calls this concept typeafe heterogeneous container .

+4
source share
2 answers

Forget what you learned at OOP School!

We went through 3 years. We have much better languages ​​now. Swift, Rust, Kotlin, Go, etc. We understand the difference between data types and values ​​and the code that controls them. The proper corporate architecture of CLEAN protects this in the field of Java. But Java simply does not provide language-level support for such patterns. The end result is heavy use of (still wonderful) things like RxJava, and annotation processors that generate code, etc. But these days it is very difficult to be happy by dragging and dropping the Java legacy. Kotlin tends to solve Java problems in such a way that Java cannot have a very minimal price: loss of tough initial compatibility (this is not Groovy).

Original answer with update below.

There are two answers.

One:

Useless cauldron!

If a class represents some large data object, it sounds as though most member variables are only containers for data. When something like this happens, it is less important to adhere strictly to the OOP information security agreement. People often confuse the purpose of this convention and end up abusing it. This simply prevents programmers from dealing with the complex and unnecessary internal workings of the object. Instead of masking the entire object, simply mask the parts that should not be confused. In the case when you simply map information from a database or act as a container for storage, a code like:

 import java.util.Date; public class Article { protected int id; protected String guid; protected String title; protected String link; protected Date pubDate; protected String category; protected String description; protected String body; protected String comments; protected Article (String articleTitle, String articleBody) { title = articleTitle; body = articleBody; } protected Article (String guid, String articleTitle, String articleLink, long publicationDate, String category, String description, String articleBody, String commentsLink) { this(articleTitle, articleBody); this.guid = guid; this.link = articleLink; this.pubDate = new Date(publicationDate); this.category = category; this.description = description; this.comments = commentsLink; } protected Article (int id, String guid, String articleTitle, String articleLink, long publicationDate, String category, String description, String articleBody, String commentsLink) { this(guid, articleTitle, articleLink, publicationDate, category, description, articleBody, commentsLink); this.id = id; } protected int getId() { return id; } protected String getTitle() { return title; } protected String getGuid() { return guid; } protected String getLink() { return link; } protected String getComments() { return comments; } protected String getCategory() { return category; } protected String getDescription() { return description; } protected String getBody() { return body; } protected void setId(int id) { this.id = id; } protected void setGuid(String guid) { this.guid = guid; } protected void setTitle(String title) { this.title = title; } protected void setLink(String link) { this.link = link; } protected void setPubDate(Date pubDate) { this.pubDate = pubDate; } protected void setCategory(String category) { this.category = category; } protected void setDescription(String description) { this.description = description; } protected void setBody(String body) { this.body = body; } protected void setComments(String comments) { this.comments = comments; } } 

.. completely disgusting.

In this case, there really is no reason to go through all the extra work just to access the members of the data object. Especially if you just use them in several external lines of code:

 public OtherClass { private Article article; public OtherClass(Article data) { article = data; } public String getArticleContents() { return (new StringBuilder()) .append(article.getTitle()) .append(article.getCategory()) .append(dateToString(article.getPubDate()) .append(article.getBody()) .toString(); } } 

Just contact members directly and save hundreds of lines of code (for example, you assumed that you tried to make).

This leads to a second answer to this question.

Two:

Construction Stank ..

Your code may be rotting. Chef Ramsey would be ashamed. That's why:

Obviously, the above OtherClass completely useless, because its functionality could (and should) be placed in the Article class instead of being contained in some other, useless, undesirable, file system-littering OtherClass . If you do this, you can forget that you even need getters and setters and OtherClass , because the things that interact with it may need only the contents of the article, and not the heading, body, etc. Separately. In this approach, the Article class hides everything from the outside world and provides only the necessary information.

Since these are two quite viable answers to your problem, there should be J on.

Use Clojure

Object Modeling

Although you can use closure for model objects , there are even better approaches. Interestingly, in a language that treats functions as first-class citizens, you can model the traditional object-oriented paradigm completely with the help of maps - as you began to figure out 30-plus-field classes in your refactoring, they were given.

Compare this with the original Article + OtherClass :

 (defn Article [] (let [id (atom nil) guid (atom nil) title (atom nil) link (atom nil) pubdate (atom nil) category (atom nil) description (atom nil) body (atom nil) comments (atom nil) set (fn [gtlp cg db cm] (do (reset! guid g) (reset! title t) (reset! link l) (reset! pubdate p) (reset! category cg) (reset! description d) (reset! body b) (reset! commments cm))) get (fn [] [@guid @link @description @comments :content (content)]) content #(str title category pubdate body)] {:get get, :set set})) 

This is the aforementioned system, which takes points from both answers and combines them into one that hides unnecessary members, includes logical functionality (a function for obtaining "content") and uses a language that does not require incredible sums of brilliant template.

Replacing a Class / Object System

Although this is a good example of how to model an object in a functional language, it is not completely idiomatic for Clojure and functional programming in general. For even simpler ways to create structured data, check out Clojure StructMaps and Reports .

Update 2017

Just use Kotlin . He cures Jan's disease in all directions. It has first class support for all of these things, and the compiler will even help you get rid of a useless template. It has been over 7 years and in stable release since February 2016. If I knew about this initially, perhaps I would not have included Closure.

+7
source

You might be looking for something like Lambok .

+3
source

Source: https://habr.com/ru/post/943827/


All Articles