DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations

Object-Oriented Solutions: Avoiding Getters

You can sometimes bypass getters in your code by substituting methods that follow business requirements. See how Object-Oriented Design can isolate components.

Robert Brautigam user avatar by
Robert Brautigam
·
Mar. 28, 17 · Tutorial
Like (76)
Save
Tweet
Share
30.66K Views

Join the DZone community and get the full member experience.

Join For Free

Alice and Bob were both working in a small team for an international broker, creating an application to react to certain events in the market. The application’s architecture centered around Trades, that represent transactions between buyers and sellers exchanging something of value on an exchange.

The initial implementation of a Trade was committed by Alice, who designed the Trade according to Object-Oriented principles, concentrating on features the Trade must have, instead of the data it must contain. It looked like this:

public final class Trade {
    private final Date timestamp;
    private final Symbol symbol;
    private final BigDecimal price;
    private final int volume;

    public Trade(Date timestamp, Symbol symbol, BigDecimal price, int volume) {
        this.timestamp = timestamp;
        this.symbol = symbol;
        this.price = price;
        this.volume = volume;
    }

    public boolean isBelow(Trade trade) {
        return price.compareTo(trade.price) < 0;
    }

    public boolean isOffMarket() {
            return !symbol.isTradingAt(timestamp);
        }
        ...
}


It had no getter or setter (accessor) methods. Instead, it offered methods that closely followed business requirements, something that was part of the language the traders also used.

Remembering The Latest Trade

Bob was working on a different part of the system, calculating differences, rises, and drops in prices, the breadth of changes occurring on certain symbols (like Google (GOOG), Apple (APPL), etc.).

To calculate how the price moved for a single symbol, Bob used a Map to remember the latest Trade for every Symbol. His initial implementation looked liked this:

private final Map < Symbol, Trade > lastTrades = new HashMap < > ();

@Override
public void handleTrade(Trade trade) {
    Trade lastTrade = lastTrades.get(trade.getSymbol());
    if (lastTrade != null) {
        ...logic with lastTrade vs.trade
    }
    lastTrades.put(trade.getSymbol(), trade);
}


Unfortunately, this code did not compile, because there was no getSymbol() method in the Trade object, therefore there was no way to get the Symbol of the Trade.

Bob opened the Trade class and to his relief noticed that the Trade does actually have the Symbol as a private field, so Bob did the following:

public final class Trade {
    private final Symbol symbol;
    ...
    public Symbol getSymbol() {
            return symbol;
        }
        ...
}


He added the “missing” getter accessor method to publish the symbol of the trade, so his code could compile. Fortunately, Alice noticed the change in a code review and approached Bob to speak about this change:

Discussing the Change

Alice: Hi Bob, I’ve noticed your change to Trade, you introduced a getter for the Symbol.
Bob: Yes, was there a conflict?
Alice: No, I was just curious what you need the Symbol for, and whether there was maybe something else in the Trade you could use…
Bob: I need the Symbol of the Trade to create a Map with the Symbol as key.
Alice: What are you using that Map for?
Bob: I need to remember the latest Trade for a given Symbol to calculate differences.
Alice: Ok, the difference calculation is already in the Trade. Could we move this “latest Trade” functionality into the Trade somehow?
Bob: I guess we could, but why would we?
Alice: Well, the Symbol is internal to the Trade, we shouldn’t just expose that for everybody.
Bob: Hm, but Symbol is a basic property of the Trade, I think exposing that does no harm, does it?
Alice: I’m not sure. I know there are features coming to change the Symbol, introducing Options and Option Chains. I’m pretty sure our Symbol concept will change eventually, and that shouldn’t impact our code elsewhere.
Bob: Ok, how could we include this in the Trade? The Trade has no concept of previous Trades.
Alice: Maybe the Trade can give you something new that can help, that does not expose the Symbol?
Bob: Well, it could give me the Map directly, I actually only need the Map.
Alice: That sounds good. Maybe we can call it a Cache, because it caches the latest Trades?
Bob: Sounds good, let’s define the interface for that…

Implementing the Trade Cache

Alice and Bob agreed to avoid exposing the Symbol property of the Trade to avoid maintenance problems later. Instead, they created the Cache in the Trade, with the explicit functionality Bob needed:

public final class Trade {
    ...
    public static Cache newCache() {
        return new MapCache();
    }

    public interface Cache {
        /**
         * Invokes the 'logic' consumer with the latest
         * and the new trade, then updates the latest trade.
         * The logic is only invoked if there was a latest trade. 
         */
        void withNewTrade(Trade newTrade, BiConsumer < Trade, Trade > logic);
    }

    private static final class MapCache implements Cache {
        private final Map < Symbol, Trade > latestTrades = new HashMap < > ();

        @Override
        public void withNewTrade(Trade newTrade, BiConsumer < Trade, Trade > logic) {
            Trade latestTrade = latestTrades.get(newTrade.symbol);
            if (latestTrade != null) {
                logic.accept(latestTrade, newTrade);
            }
            latestTrades.put(newTrade.symbol, newTrade);
        }
    }
}


With this new feature from Trade, Bob’s code became simpler, but more importantly, it became independent of the internal structure of Trade:

private final Trade.Cache tradeCache = Trade.newCache();

@Override
public void handleTrade(Trade trade) {
    tradeCache.withNewTrade(trade, (lastTrade, newTrade) - > ...logic...);
}

Conclusion

Object-Orientation is a way to isolate objects to protect against changes that can propagate through code unchecked. The main tool to reach this isolation is encapsulation and data hiding. Following these principles requires objects to take on real responsibilities, and avoid exposing the details of these responsibilities, like the “properties” or other objects that are needed for them.

This article shows one possible way to avoid exposing properties by moving a functionally closed, meaningful feature into the object best suitable for it. Although these kinds of solutions might be unfamiliar to developers trained or experienced in other paradigms, they are absolutely necessary for creating an Object-Oriented Design.

Alice (software)

Published at DZone with permission of Robert Brautigam. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Simulating and Troubleshooting BLOCKED Threads in Kotlin [Video]
  • How To Build a Spring Boot GraalVM Image
  • Low-Code Development: The Future of Software Development
  • Accelerating Enterprise Software Delivery Through Automated Release Processes in Scaled Agile Framework (SAFe)

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: