0

Let's say in a simple shopping application there are a Customer class, Seller class, and Trade class, and the code looks simply like this(to illustrate my question):

 public class HelloWord {

    public static void main(String[] args) {

        Customer customer = new Customer();
        Seller seller = new Seller();

        Trade trade = new Trade(customer,seller);
        trade.buy(2);
    }

}

class Customer {
    private ArrayList<String> itemCart = new ArrayList<String>();
    private int gold = 100;

    public void setGold(int amount) {
        if (gold - amount >= 0) {
            gold -= amount;
        }
    }

    public int getGold() {
        return gold;
    }

    public void add(String item) {
        itemCart.add(item);
    }
}

class Seller {
    private ArrayList<String> itemCart = new ArrayList<String>();
    private ArrayList<Integer> itemsPrice = new ArrayList<Integer>();

    public int getItemPrice(int itemID) {
        return itemsPrice.get(itemID);
    }

    public String getItemById(int itemID) {
        return itemCart.get(itemID);
    }
}

class Trade {
    private Customer customer;
    private Seller seller;

    public Trade(Customer customer, Seller seller) {
        this.customer = customer;
        this.seller = seller;
    }

    public void buy(int itemID) {
        if (seller.getItemPrice(itemID) <= customer.getGold()) {
            customer.add(seller.getItemById(itemID));
            customer.setGold(seller.getItemPrice(itemID));
        } else {
            System.out.println("You don't have enough money to buy this item");
        }
    }
}

My question is "Do the "setGold" and "add" methods expose the attruputs?" i don't want the user to be able to modify the itemCart neither the gold attribute by just call the add method or setGold on his own, but i want to be able to access them to modify the attruputs using other methods, in this case from "buy" method in Trade class.

My question in other words: "Should i be concerned if these method could be accessed from the main method or that is normal and does not violate data integrity?"

5 Answers5

1

Short ans no, since itemCart is private, runtime caller can't access that directly.

Long answer https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html.

stackguy
  • 188
  • 5
1

The purpose of data encapsulation is to hide the implementation of an object by ensuring that the contents of the object are only modifiable through the interface of the object. By that definition - the Trade object inherently breaks encapsulation because of its constructor. Since you are passing in references to Customer and Seller, any invariants that the Trade object is supposed to maintain can be broken simply by the person modifying the customer or seller directly.

It's hard to know what the best alternative is without more context, but a possible fix could be rather than passing in the Customer and Seller on construction of the Trade object, passing them into the buy function. Another solution could be to attach the buy function to the Customer or Seller objects instead, getting rid of the Trade object altogether. Generally classes in OOP represent objects, and not actions (outside of certain design patterns).

dcco
  • 75
  • 8
  • I think you are right, the reason that i created Trade class because i think it is "Control object" "control objects that receive events and co-ordinate actions as the process moves to the solution space" Another reason that i found ATM project that has Transaction as abstract class and have Withdraw and Deposit as subcasses, so i just thought that is semilar senario, is't it? The ATM example : http://www.math-cs.gordon.edu/courses/cs211/ATMExample/ UML: https://drive.google.com/file/d/1kFlfD8Zhv8eTeU2gYWa0HlY0yeKGfYlA/view?usp=sharing – Muntaser Abukhadijah May 29 '20 at 20:33
  • @MuntaserAbukhadijah So the reason the ATM project has a Transaction class is because transactions are not instantaneous actions, but requests made by the ATM to the bank that may or may not take affect. AKA they may be rejected / fail for some reason. However one expectation is that the user is never expected to interact directly with Transaction objects, they are used by the ATM to communicate with the bank. In that case the ATM is the "Control object" / controller, which ensures the user can't mess up the bank. .. cont – dcco May 30 '20 at 01:08
  • In your case, you dont have a single central bank, but a lot of disjoint Customers / Sellers. If you ever did have a single system of customers + sellers and wanted to make sure they stayed consistent with each other, that's when a Trade object would become useful. And even though Trade objects technically break encapsulation, they're part of a greater implementation that is encapsulated by the Controller / Model (ATM / Bank). All that to say - it's not a crime to do things the way you are doing them - just know that in a vacuumn, you do run the risk of breaking encapsulation this way. – dcco May 30 '20 at 01:12
1

The setter itself doesn't expose anything. So long as you're not exposing the exact fields (e.g. you're not doing getItemsPrice and returning an ArrayList), you're fine.

All bets are off with reflection, however. But, that's not the concern here.

Makoto
  • 104,088
  • 27
  • 192
  • 230
1

I suggest you should change the setter to common form, and you can make it private.

private void set(int amount) { 
    this.amount = amount;
}

public boolean spend(int amount) { 
    if (gold - amount >= 0) { 
        gold -= amount;
        return true;
    } else {
        return false;
    }
}

In buy method, invoke spend().

jean
  • 11
  • 1
1

You are not getting the answer you expect because the question is a bit confusing as it is now. Reading through it carefully, you are not asking if the main method can access/change the Customer properties directly, but if the main method can use the add and setGold methods to change those properties. Also, the Seller class is just adding entropy as it's not relevant for the question.

Breaking it down:

Do the "setGold" and "add" methods expose the attruputs?

The attributes themselves are not exposed but both methods allow modifying those attributes from the outside since they are declared as public.

i don't want the user to be able to modify the itemCart neither the gold attribute buy just call the add method or set gold on his own

This is possible with your current code as both add and setGold are public. That's exactly the purpose of public.

but i want to be able to access them to modify them using other methods, in this case from "buy" method in Trade class

If you want add and setGold to be visible only to the Trade class, one option is to put Trade and Customer classes in the same package as in the following example:

com.example
    shopping
       |--- Customer.java
       |--- Trade.java
    application
       |--- HelloWorld.java

And then make both methods package-private, like so:

public Customer {
    // ... properties and other methods 

    void setGold(int amount) {
        if (gold - amount >= 0) {
            gold -= amount;
        }
    } 
    void add(String item) {
        itemCart.add(item);
    }
}

The difference to your code is that neither method contains a visibility modifier (removed the public keyword), making them package-private, thus only accessible from the same package.

With that structure and package-private methods in the Customer class, if you call the add or setGold from the main class you will get a compiler error:

add(java.lang.String) is not public in com.example.shopping.Customer; 
cannot be accessed from outside package 

But you can still access it from the Trade class because it's in the same package.

rph
  • 2,104
  • 2
  • 13
  • 24