Static member inheritance and access through child member in Java - java

I have the following case here:
Room { price; }
|
------
/ \
standard suite
I want to set price the of standard rooms in such a way that it remains static in all instances of standard and must not affect suite's price and vice versa. I have tried keeping price in Room class static and accessing it via getter and setter in child classes but it doesn't work. I also am reluctant to make price members in each child class because I don't like that solution. Maybe there's another beautiful OOP solution to it.

Having a separate static field in both the Room and Suite classes is the quickest/easiest solution.
Room
_________|_______
/ \
Standard Suite
| |
`static int price; `static int price;
Alternatively, you could create a static Map<Class<? extends Room>, Integer> in the Room class which stores the base price of each Room type.
public class Room {
private static Map<Class<? extends Room>, Integer> prices =
new HashMap<>();
public final int getRoomBasePrice() {
// If a Room subclass does not have a specific base price, return
// 1000 by default.
return Room.prices.getOrDefault(this.getClass(), 1000);
}
/** Sets the base price for the specified Room type.
*/
public final void setRoomBasePrice(int price) {
Room.prices.put(this.getClass(), price);
}
}
Using the above code will ensure the price stays constant across all instances of the class.
mySuite.setRoomBasePrice(2000);
(new Suite()).getRoomBasePrice(); // -> 2000
EDIT: After reconsideration, I realise that using static is not the correct method to solve the problem as it makes the code brittle and difficult to change.
The best method would be to have a separate RoomPriceService class, which provides a lookup for obtaining the price of a specific room type.
public class RoomPriceService {
private Map<Class<? extends RoomType>, Integer> prices;
public RoomPriceService(int defaultPrice) {
this.prices = new HashMap();
}
public void setPriceOfRoomType(Room r, Integer price) {
this.prices.set(r.getClass(), price);
}
public Integer getPriceOfRoomType(Room r) {
// You can expand on this code by adding setters/getters for
// setting and getting the default room price.
return this.prices.getOrDefault(r.getClass(), 100);
}
}
This way, you can have multiple RoomPriceService instances which can store prices for different circumstances (for example, you could have a RoomPriceService for each season, or a RoomPriceService for different sale promotions, etc).

Related

Does setter method expose the attributes?

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?"
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.
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.
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).
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.
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().

How can I cleanly link Enums to the static information within classes?

For the sake of clarity, I will do my best to frame the question through the lens of an example, rather than a code dump.
Before getting into my example, my question is as follows: How can I write a method that takes an enum as a parameter, and returns the static-information stored in an object (which inherits from a communal parent, where the static information is defined).
The example:
I am creating a game which includes many player-skills. These skills are created via an object tree, with the following inheritance (SomeSkill represents any of a dozen or so skills):
Skill > ActiveSkill > SomeSkill and Skill > PassiveSkill > SomeSkill
Psuedo-code for the class Skill:
Class Skill{
static string name = "Default Skill"
int level;
Skill(int level){
this.level = level;
}
static getName{
return name;
}
}
Name is static, since the name of the skill shouldn't change, regardless of the intance. In my actual implementation, Skill also includes static information description, and id.
Actual implementation of enum:
public enum SkillType
{
basic, speed_buff, leap, beat_down
}
The problem:
What I am struggling to do is write a method that takes a SkillType enum as an argument, and returns a usable Skill object (NOT an instance of the Skill object)
In Psuedo code: As an example, if I wanted to loop through the Enum and print out the names of all skills...
method getSkillClass(SkillType skillType){
if(skillType == beat_down) return BeatDown
if(skillType == leap) return Leap
...
}
for(e : SkillType.getKeys){
print(getSkillClass(e).getName);
}
My current "solution" would be to create a map that matches SkillType to a list of instanced out Skills, with all non-static skill information set to default values.
This seems like an abuse of the system though.
How can I cleanly link my enum-list to the static information (the non-static information can be ignored) in my various skill classes?
Java enums allow a constructor that can be used to associate data to the enum:
class Skill {
...
}
public enum SkillType {
Basic(new Skill(...)),
SpeedBuff(new Skill(...)),
Leap(new Skill(...)),
...
private final Skill skill;
public SkillType(Skill skill) {
this.skill = skill;
}
public Skill getSkill() {
return skill;
}
}
For further information, look at the 'Planet' example in the official enum documentation.
As #Alex mentioned, you can use enum constructor, but instead of creating an instance for linking, you can link the Class type if you don't want to create instances:
public enum SkillType {
Basic(Basic.class),
SpeedBuff(SpeedBuff.class),
Leap(Leap.class),
...
private final Class skillClass;
public skillClass(Class skillClass) {
this.skillClass = skillClass;
}
public Class getSkillClass() {
return skillClass;
}
}
You can then use reflection techniques to get the static field information as explained here.

Java - Possible use of Strategy Design Pattern?

public class ClassA_V01 {
private String name;
private int age;
// getter and setter
}
public class ClassA_V02 {
private String name;
private int age;
private int gender;
// getter and setter
}
public static void main(String[] args) {
SomeClass classA = new ClassA_V01();
classA.setName("myName);
classA.setAge(99);
performLogic(classA);
// OR
SomeClass classA = new ClassA_V02();
classA.setName("myName);
classA.setAge(99);
classA.setAge(1);
performLogic(classA);
}
public void performLogic(SomeClass classA) {
// do something
}
For strategy pattern to work, both classes must implement the same methods defined in the interface. But what if the classes need to have different fields and methods?
In my example, ClassA_V01 and ClassA_V02 are the same class except that one has more attribute "gender"
How does one implement the above such that classA can be equals to either ClassA_V01() or ClassA_V02?
"...For strategy pattern to work, both classes must implement the same methods defined in the interface. But what if the classes need to have different fields and methods?..." really this is not a criteria for strategy pattern.
Strategy pattern's intent is to identify and make family of algorithms interchangeable. If you read the pattern's documentation carefully, Strategy can be used when many related classes differ only in their behavior.
Appropriate decomposition is the key for better (extendable) design. A typical (but primitive) solution to Employee assignment, sub-classing tempEmp and permanentEmp types will put us in trouble and will not allow temp employee to become permanent in its life time (which has no meaning in real terms). This happens because we miss an important point- each employees employeeness is not different, they are all same type of employees with different pay policies. (same logic can be extended for Leave policy and so on)
This becomes simple if all types of employees have Salary computation based on same components (same state). But your question is what if TempEmployee gets only basicPay whereas PermanentEmployee gets basicPay as well as travelAllowance (additional attribute which is not present for TempEmp). This can be modeled by a combination of simple inheritance hierarchy along with strategy taking care of computation algorithm dependent upon Employee's (aka. Context) attribute (age)
public class Employee {
//name and id
private PayPackage payPackage;
private int age;
PayPackage strategy;
public double computeSalary() {
return payPackage.computePay(age);
}
//get/setPayPackage(...)
}
public abstract class PayPackage {
private double basicPay;
abstract public double computePay(int age);
protected double getBasicPay(){
return basicPay;
}
}
public class TempPayPackage extends PayPackage{
#Override
public double computePay(int age) {
double veteranAllowance = 0;
if (age > 40) {
veteranAllowance = 2000.00;
}
return getBasicPay() + veteranAllowance;
}
}
public class PermanentPayPackage extends PayPackage{
private double travelAllowance;
#Override
public double computePay(int age) {
double veteranAllowance = 0;
if (age > 40) {
veteranAllowance = 5000.00;
}
return getBasicPay() + travelAllowance + veteranAllowance;
}
}
Important thing to remember is Design patterns never work alone or as an alternative, they work hand in hand with Object oriented code and other patterns.

Design for a shop - multiple product classes

I was reading about Spring and encountered an example consisting of an abstract product class with name and price fields.
Next, there is a Battery class which extends the Product class and adds a rechargable field. Then, a CDDrive class (also) extending Product but adding a capacity field.
In the real world when we often have products having many disparate attributes, how does one model arbitrary products with arbitrary properties and fields?
Does having a class for each product make sense?
So, can you guys please suggest a pattern for achieving this?
Thanks,
Ouney
Good question. We had a similar situation before where we had GUI components that shared many of their abstract parent, but each page had its own set of labels that weren't shared by others. The standoff was on. We found it silly to just keep creating subclasses because of the mutually disjoint properties they had. What did it for us was maps. First, to have a subclass is to have one or more distinguishing properties that are fist class objects. Rechargeable for batteries and capacity for cd drives in your case. Then for the properties one can't think of at the time of building, or simply differ in minor naming conventions, use maps. I demonstrate with the example below.
The product:
public abstract class Product {
String name;
Double price;
Map<String, Object> propMap;
public Product(String name, Double price) {
this.name = name;
this.price = price;
propMap = new HashMap<>();
}
public void add2propMap(String key, Object value) {
propMap.put(key, value);
}
public String toString() {
return "Product [name=" + name + ", price=" + price + ", propMap=" + propMap + "]";
}
}
The CdDrive:
public class CdDrive extends Product {
String capacity;
public CdDrive(String name, Double price, String capacity) {
super(name, price);
this.capacity = capacity;
}
}
The Battery:
public class Battery extends Product {
Boolean rechargable;
public Battery(String name, Double price, Boolean rechargable) {
super(name, price);
this.rechargable = rechargable;
}
}
Then a client:
public class Client {
public static void main(String[] args) {
List<Product> productList = new ArrayList<>();
Battery energizer = new Battery("Energizer", 12d, true);
energizer.add2propMap("numInPackage", new Integer(8));
energizer.add2propMap("make", "US");
productList.add(energizer);
CdDrive superDrive = new CdDrive("Apple Drive", 200d, "200 GB");
superDrive.add2propMap("type", "External");
superDrive.add2propMap("expandable", false);
productList.add(superDrive);
productList.forEach(p -> System.out.println(p));
}
}
Which gives this when run:
Product [name=Energizer, price=12.0, propMap={numInPackage=8, make=US}]
Product [name=Apple Drive, price=200.0, propMap={expandable=false, type=External}]
This setup made the architecture scalable, maintainable and modifiable. The map keys always reported what was in there in case in doubt. Adding is easy and so is modifying.
Does having a class for each product make sense?
In real life situation, it rarely makes sense. They are just making up some example to make you get the feeling of it.
Just imagine your online shop sells CD players, now you want to add some MD players in your product list, and you need to change your code and redeploy the application just because of it. Non-sense huh?
Unless you have bunch of specific function for some specific type of product, having a dedicated class for such type of product will make sense. (e.g. Product, PhysicallyDeliverableProduct something like that. Still there are better way to design it though)
In real life, the way to solve the issue in your question, is mostly by designing your Product to keep some arbitrary properties (e.g. keeping a Map<String,Object>, so you can put ["rechargeable", true] for a battery you add on your site.
Design pattern? I think what you are looking for is still far from required to make use of patterns. Personally I will suggest you to take a look on the book "Analysis Pattern" by Martin Fowler. You may not be able to use the design in it directly, but it give you feel on what real life design looks like
Does having a class for each product make sense?
To me it absolutely makes sense to have separate classes for separate products.
That makes your code more loosely coupled. In future if you want to change the implementation of a particular product, changing the code won't mess up the implementation of other products if you have a separate class for that. The generic methods & properties you can put in an abstract class.
a pattern for achieving this?
You might want to look at the Factory & template pattern.
You can create an interface Product & all the classes will implement that interface & define their own implementations.
Use abstract class only when you want to provide a default behaviour to your methods. For an instance have a look at the template pattern here.
An abstract class game is created which defines the play method. initialize & startPlay etc can have their respective definition in the subclasses but the play method will always run the other methods.
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
//template method
public final void play(){
//initialize the game
initialize();
//start game
startPlay();
//end game
endPlay();
}
}
If you don't intend to provide any default behaviour rather just declare the properties & methods in an interface Product & let the classes implement that.
interface Product{
String NAME="defaultName";
Integer PRICE=5;
initialCost(); // example of a generic method
}
//Note that name & price if you declare those in interface will be treated as constants.
class Battery implements Product{
Boolean rechargable =false;
public void initialCost(){
//method definition
}
}
class CdDrive implements Product{
Integer capacity = xxxx;
public void initialCost(){
//CdDrive method definition
}
}
You can create the objects as
Product product = new Battery();
Product nextProduct = new CdDrive();
this makes your code loosely coupled. Also known as programming to an interface.

Assigning enum to Items

Some background on the project: I am attempting to craft a space/sci-fi combat sim game with tabletop rpg style dice mechanics cranked up to 11 on the complexity scale, but still being transparent about the die rolls going on under the hood. I'm currently using the Star Wars Saga Edition combat rules as a basis.
Currently I'm trying to figure out a way to assign traits to vehicle.(possibly stored as a class for each vehicle) Each trait is an enum so that it can store multiple pieces of information. Here is the code I have for size categories:
public enum VehicleSize {
LARGE(1,"Speeder bike",5),HUGE(2,"Small Fighter",10),GARGANTUAN(5,"Tank, Medium Fighter",20),COLOSSAL(10,"Imperial Walker, Light Freighter",50),
COLLOSSALfrigate(10,"Corvette, Frigate",100),COLLOSSALcruiser(10,"Imperial-class Star Destroyer, Cruiser",200),
COLLOSSALstation(10,"The Death Star, Space Station",500);
private final int refMod;
private final int threshMod;
private final String desc;
VehicleSize(int reflexModifier,String example,int thresholdModifier)
{
refMod = reflexModifier;
desc = example;
threshMod = thresholdModifier;
}
public int getRefMod() {
return refMod;
}
public String getDesc() {
return desc;
}
public int getThreshMod() {
return threshMod;
}
}
My question is such: How do create vehicle profiles in such a way that I can assign this and similar enums as traits?
For practically all purposes, a field whose type is an enum class is no different than a field of any other object type, like Integer or String.
Create a private field, add a getter and setter, or if the field is final (likely in your case, because a vehicle instance can't change its type), add it as a constructor parameter and remo e the setter.
public class Vehicle {
private final VehicleSize vehicleSize;
// other fields
public Vehicle(VehicleSize vehicleSize) {
this.vehicleSize = vehicleSize;
}
public VehicleSize getVehicleSize() {
return vehicleSize;
}
// rest of class
}
There is nothing mysterious about an enum, other than the number of different instances of it are known at compile time (and a few more things, but nothing scary).
To add this into a class, you can use it like any user defined type.
public class MyClass {
private MyEnum myEnum;
}

Categories

Resources