Use of static classes to represent different forms of currency - java

I coded myself an issue last night while developing a small game. The other day, I convinced myself that I should devote some of my spare time to something productive, so instead of gaming I decided I would start working on a text-based rpg. I came, then, to the issue of representing currency in game. Now, because this is just for fun, I'd like to challenge myself a bit. Instead of just representing currency as a single value ( eg: a single type of coin called "gold piece" isn't the only kind of coin in the game .)
What I decided to do was create 4 types of coin - pence, copper, denar, and oren. All 4 of the coins have values such as weight, volume, material, and name. Furthermore, the coins have exchange rates, which determine their relative values. The point of this was to permit different usages of what would otherwise be a boring old currency. My issue is that I am unsure how to implement it.
What I arrived at last night was 4 classes ( Pence, Copper, Denar, Oren, ) extending an abstract class Coin. Coin contains a lot of protected static elements, such as DENSITY, VOLUME, NAME, EXCHANGE for all 4 subclasses.
The constructors for the sub-classes look like this:
public Coppers() {
super();
super.metal = COPPER_METAL;
super.name = COPPER;
super.setVolume();
super.setDensity();
super.setWeight();
}
And the methods in the super class look like this:
protected void setDensity() {
switch( getMetal()) {
case "copper":
this.density = DENSITY_COPPER;
break;
case "silver":
this.density = DENSITY_SILVER;
break;
case "gold":
this.density = DENSITY_GOLD;
break;
default:
this.density = DENSITY_COPPER;
break;
};
}
This seems terribly... wrong. I'm not sure what the best practice would be. I asked my friends about using a static class to hold these values, and received mixed responses. The POINT of these classes though, is important. Imagine the player class has an object called Purse, which keeps track of the number of different types of coins. With their Purse, the player can exchange coins at banks, purchase goods, and sell goods for coins. It wouldn't make any sense to hold a Set of all instantiated coins, right? I just need the information, and the methods. Does implementing a static class make sense, then? How can I get all 4 coins working best, when they all share so many properties?

What you may do in this case is to use an Enum. You enumerate the constants you need, give them a type through their constructors.
Now that we have their type, we can compare it to the string you're handling within your code, if none of the types match, we set it to Density.COPPER by default.
Density density;
protected void setDensity (String metal) {
for (Density d : Density.values()) {
if (metal.equals(d.getType())) {
this.density = d;
return;
}
}
this.density = Density.COPPER;
}
enum Density {
COPPER("copper"),
SILVER("silver"),
GOLD("gold");
String type;
Density(String s) {
type = s;
}
public String getType() {
return type;
}
}

Let's work backwards here.
Imagine the player class has an object called Purse, which keeps track of the number of different types of coins. With their Purse, the player can exchange coins at banks, purchase goods, and sell goods for coins.
What this implies:
public class Purse {
private final List<Coin> coins = new ArrayList<>();
}
This tells me that enums are not sufficient (enough) here. An enum in this context describes multiple states; what you're looking for are actual objects which can hold values that you need to do some calculations on.
If our intention is to hold on to this currency, I don't see anything wrong with some central object to describe it.
In my mind, using an abstract class for this is probably fine, but you're missing a critical component: a factory to create the type of coin you want. You'll also want to reduce the responsibilities of the coin altogether - it's fine for a coin to know its value, but it shouldn't care what its value is relative to other coins; that's the responsibility of some kind of exchange object which intends to produce a number of coins based on the value of your given coin.
So let's write the constructor for the abstract class. If we're thinking of creating a generic coin, we need to know its volume, density, and weight. The name is provided by virtue of its class name, so you really shouldn't need to worry about that; you can extract it later.
If you want some sort of coin hierarchy, you can leverage Comparable; state the ordering there instead of through enumeration.
public abstract class Coin implements Comparable<Coin> {
protected final int volume;
protected final int density;
protected final int weight;
public Coin(int volume, int density, int weight) {
this.volume = volume;
this.density = density;
this.weight = weight;
}
public int getVolume() {
return volume;
}
public int getDensity() {
return density;
}
public int getWeight() {
return weight;
}
}
This describes the barebones Coin type.
For an example, let's describe the Copper type here as well. This code makes the assumption that coin of the same type is comparable otherwise it demotes itself (with Copper being at the bottom of the list).
Observe a few things:
We preserve a lot of the original logic from the parent class
We override compareTo (because we must), and we let that drive the chief way of ordering on coins.
This does not describe any sort of conversion since the coins really shouldn't need to know about that. They have no value between coins until it's time to actually convert them. Think foreign exchange.
public class Copper extends Coin {
public Copper(final int volume, final int density, final int weight) {
super(volume, density, weight);
}
#Override
public int compareTo(final Coin otherCoin) {
if(otherCoin instanceof Copper) {
return (volume - getVolume()) + (density - getDensity()) + (weight - getWeight());
}
// assume Coppers are worth the least
return Integer.MIN_VALUE;
}
}
The other currencies are left as an exercise for the reader.
The last thing I want to cover is some form of generator for all of your currencies. This is where a bit of reflection magic can really help to invoke the constructor you care about.
I've also made this return an Optional<T extends Coin> so that, in the event the generation fails for some reason, you have an optional to work with instead of null.
public class CoinFactory {
private CoinFactory() {
}
public static <T extends Coin> Optional<T> generateCoin(int weight, int volume, int density, Class<T> clazz) {
Optional<T> coin = Optional.empty();
try {
coin = Optional.of(clazz.getDeclaredConstructor(int.class, int.class, int.class)
.newInstance(weight, volume, density));
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
return coin;
}
}
You can use this main method to smoke test it:
public static void main(String[] args) {
final Optional<Copper> x = CoinFactory.generateCoin(10, 20, 30, Copper.class);
if(x.isPresent()) {
System.out.println(x.get());
}
}

Put a private Map in Coin for Density, with keys being "copper", "gold", and "silver", and the values being the DENSITY_ constants. setDensity() should simply run:
this.density = densityMap.get(getMetal());
Or better yet, scrap setDensity(), and instead simply have getDensity(), which returns densityMap.get(getMetal()).

Since the four kinds of coin don’t differ in behavior, one class is enough. I suggest this:
public enum Coin {
PENCE, COPPER, DENAR, OREN;
private static final String[] METAL = { "copper", "copper", "silver", "gold" };
private static final int[] VALUE = { 1, 12, 60, 360 };
public String getMetal() {
return METAL[ordinal()];
}
public int getValue() {
return VALUE[ordinal()];
}
}

Related

Which design pattern to use (Active and passive methods)?

I have a player which can feed a dog or chop a tree.
Below are the classes I have written:
public class Dog {
private int health;
public void feed(Food food){
health = health + food.getNutritionalValue();
}
}
public class Player{
public void feed(Dog dog, Food food) {
dog.feed(food);
}
Player and Dog both have methods that are "active".
Player feeds the dog and dog starts eating the food (I am not really sure if it is good to couple methods in this way).
On the other hand, I have tree. And player is able to chop the tree.
public class Player{
public void chop(Tree tree) {
//At this point I am not sure
}
I am not sure if I would use getters and setters of Tree class to interact with the Tree.
Or if I should write an own method for this because the tree gets chopped so it is nothing really active I would call.
So, in the end, there would be two or more kinds of implementations but the two I am thinking of are:
tree.setAmountofWood = x
or
tree.gettingChopped(Damage int)
I think I should make an own method for this chopping-process.
Or is there any design principle I should follow?
I see 3 principles here,
SRP - It is the responsibility of the Tree to get chopped and fall down, but to cut is the responsibility of the Person!
Demeter's law - looks good from my POV.
OCP - The tree must be able to do further actions when get cut.
So you must use
tree.gettingChopped(Damage damage)
To your code:
The method Dog.feed is wrong, rename it to Dog.eat because the Dog is not feeding, the dog is eating. By the way, the food must reduce its NutritionalValue.
The health is an integer value, this is bad because in reality there is nothing like a numeral health. We may have a handicapped numeral value in percent, but this is more a byte who not can be in negative value. You should create a custom class for the Health! This way your code is open(OCP) for extensions like to be toxified or depresive.
I would start from something like this.
Tree can grow and receive damage.
public class Tree {
private int lumber;
public Tree(int size) {
this.lumber = size;
}
public void grow() {
this.lumber++;
}
public void grow(int size) {
this.lumber += size;
}
public int receiveDamage(int damage) {
int lumber = 0;
if (damage > this.lumber) {
lumber = this.lumber;
this.lumber = 0;
} else {
lumber = damage;
this.lumber -= damage;
}
return lumber;
}
}
Food just stores nutritional value.
public class Food {
private int nutrition;
public Food(int nutrition) {
this.nutrition = nutrition;
}
public int getNutritionalValue() {
return this.nutrition;
}
}
I'm not sure if all types of player can chop trees, so I created a class to separate responsibilities. You can move methods to the Player class if you like.
public class Woodcutter extends Player {
public int chop(Tree tree) {
// lumber amount may depend on a tool,
// i.e. axe, chainsaw, etc.
return tree.receiveDamage(10);
}
// fell down the tree
public int fell(Tree tree) {
int result = 0;
int lumber = 0;
do {
lumber = chop(tree);
result += lumber;
} while (lumber > 0);
return result;
}
}
Somewhere in your code
// create a tree and let it grow for a while
Tree tree = new Tree(10);
tree.grow(90);
// Start chopping
Woodcutter woodcutter = new Woodcutter();
System.out.println("Lumber received: " + woodcutter.chop(tree));
System.out.println("Lumber received: " + woodcutter.fell(tree));
Dog dog = new Dog();
Food food = new Food(5);
woodcutter.feed(dog, food);
I wouldn't dive into passive/active methods here. An 'active tree' may indeed be a misnomer.
I would rather consider calling an object's method as passing a message to the object. And you apparently need to send the message to the tree that it is currently being cut by someone, and let the tree decide when to e.g. fall() or to bend(), or to shake().
The tree has some internal state (strength? thickness of its trunk? health?). 'Sending a message' to the tree means to call its method, e.g. beingCut(), which in turn deteriorates the state of the tree. After the state of the tree reaches a certain limit, other actions (=consequences of tree's bad state) may be started by the tree.
Of course, as in every iteration of your main loop you tree has also the chance to get the message to grow(), so its state may improve a little each time, so eventually it may even recover from being only partially cut and reach its initial, perfect state back.
So, yes, while trees seem rather passive, they still react to messages/stimulus. :-)

java: confusing instructions, shopping cart program

My teacher gave me confusing instructions on this coding assignment. If you guys could help elaborate or give me tips, I'll provide what I have.
First of all the program is where I have to make 2 classes that will work with a big class to produce a shopping list where you can edit how much of each item you want. Have to take the name of an item, how many times its purchased, and how much each one costs.
I finished my first class, I'll post the entire coding and rules for the coding at the bottom of this question.
Okay so here's what I have. I'll go step by step.
Rule 1: A field private Purchase[] as an array of purchases.
Another int field that tracks how many purchases have actually been made
So I made this:
private int Purchase[];
private int purchaseCount;
Rule 2: Negative values do not make sense, so just reset those to zero if provided by user
Okay so in the first program I had to do the same thing, but I'm confused how to do it now.
I implemented the "reset to zero" in the modifiers, but now my teacher is not asking for modifiers. Am I supposed to put them anyway? I know I just have to put an "if blahblahblah < 0, then blahblahblah = 0" thing, but how do I go about that?
Rule 3: Accessor .length() method that returns your int field for how many purchases
public int Purchase(){
return ;
}
I guess this is about all I know for that. I know I have to return something, not sure how to use length though. And I think there's a parameter.
Final Rule 4: Accessor .get(int) for the Purchase array, which needs a parameter that will index the array. So get(0) returns the first element (a Purchase object) of the array.
I think I understand this, but since I don't know how to do the last step, I haven't tried this yet. ".get(int)" what? So an accessor where I perform a .get(int) inside it? I don't know much about accessors, this is why I need this help. The rest of the program seems pretty simple for me, but this initial stuff confuses me. Thanks.
Rules for already completed class:
Three fields, a String for name of the purchase, int for units purchased, and a double for cost per unit.
• Standard accessors and modifier methods for each field.
• Negative values are not allowed, so change those to zero in all cases.
• Constructor to initialize these three fields (String, int, double) in that order.
• Constructor overload, (String, double) assumes the int quantity is zero.
• Default constructor that assumes name is “” and numbers are zero, must call the three argument constructor.
• A getCost method that is simply the number of units purchased times unit price.
• A toString method return a String with the item name followed by the unit price in parentheses
Completed program:
public class Purchase {
private String purchase;
private int unitsPurchased;
private double costPerUnit;
// Accessors
public String purchase() {
return purchase;
}
public int unitsPurchased() {
return unitsPurchased;
}
public double costPerUnit() {
return costPerUnit;
}
// Modifiers
public void setPurchase(String purchase) {
this.purchase = purchase;
}
public void setunitsPurchased(int unitsPurchased) {
if (unitsPurchased < 0) {
unitsPurchased = 0;
}
this.unitsPurchased = unitsPurchased;
}
public void setCostPerUnit(double costPerUnit) {
if (costPerUnit < 0) {
costPerUnit = 0;
}
this.costPerUnit = costPerUnit;
}
//constructors
public Purchase() {
this("", 0, 0);
}
public Purchase(String initialPurchase, double initialCostPerUnit) {
this.purchase = initialPurchase;
this.unitsPurchased = 0;
this.costPerUnit = initialCostPerUnit;
}
public Purchase(String initialPurchase, int initialUnitsPurchased, double initialCostPerUnit) {
this.purchase = initialPurchase;
this.unitsPurchased = initialUnitsPurchased;
this.costPerUnit = initialCostPerUnit;
}
//end of everything I am sure about
//beginning of unsurety
public static double getCost(String purchase, int unitsPurchased, double costPerUnit) {
return unitsPurchased * costPerUnit;
}
public static String toString(String purchase, int unitsPurchased, double costPerUnit){
return purchase + costPerUnit;
}
}
Okay, so first rule 1 the code should look like:
private Purchase[] purchases;
private int purchaseCount;
Remember, in this case since you've already defined Purchase in your other java file, you're using it as a datatype, not as an identifier.
For rule 2, you're going to want that if statement in the access methods for purchaseCount as well as in the constructor.
Rule 3 is extremely vague...but my best guess is your teacher wants you to define a length method for that class, so that when you call say purchases.length() it returns the purchase count.
Again, rule 4 is vague, but my best guess is you need to define a get method for that class that just returns a value from your private purchases array using a given index.
Something like this:
public Purchase get(int index) {
return purchases[index]
}
I hope this helps and good luck!!

What is the Java standard for object cohesion? Does too much information in an object mean bad design? - see example

I'm creating a project which models an airport landing system. I have a plane object which stores all the information I need to sort the plane into a queue and store in a database. All the vital information is included in the object but I have also included the co-ordinates for each plane. My issue is that it may not be considered cohesive because each plane does a lot of different things.
I just want to know if this is considered bad design or is there a better way to do this?
Also, what is the "rule" for cohesion inside of objects? is there a specific design pattern that can maybe deal with this?
public class Plane extends Aircraft {
/*
* Flight status should only take one of these enum values
*/
private static enum Status {
REGISTERED, IN_QUEUE, LANDING, LANDED
};
// Set aircraft status to REGISTERED when created
private Status status = Status.REGISTERED;
private double fuelLevelPercentage;
private int passengerCount;
private int aircraftNumber;
private String airlineCompany;
private String departureAirport;
// This is used by the constructor to assign a random city to each new Aircraft
private final String[] cities = { "Rome", "Berlin", "Heathrow",
"Edinburgh", "Cardiff", "Dublin", "Stansted" };
// Used to set airline companies
private final String[] airLineCompanies = { "Easyjet", "Ryanair",
"British Airways","Flybe","Air Lingus", "Virgin" };
// Random number generator used by the constructor
private Random rand;
// Thread for this instance of Aircraft
private Thread aircraftThread;
// Radius of path when flying in circle (km?)
private final double FLIGHT_RADIUS = 10;
// Time taken to complete one complete loop (ms)
private final double FLIGHT_PERIOD = 120000;
// Angular frequency omega (rad/s)
private double OMEGA = 2 * Math.PI / FLIGHT_PERIOD;
// Time taken between being directed to land, and landing (ms)
private int TIME_TAKEN_TO_LAND = 30000;
// Time take to use one percent of fuel (ms)
private double time_taken_to_use_one_percent_of_fuel = 30000;
// variable to keep track of time since instantiated (ms)
private int time = 0;
// The aircraft Thread sleeps for TIME_STEP between updating
private final int TIME_STEP = 20;
private int time_when_called_to_land;
private int hour_of_arrival;
private int minute_of_arrival;
/*
* Set coordinates at time zero
*/
private double x_coord = 0;
private double y_coord = FLIGHT_RADIUS;
private double altitude = 1000;
/*
* Used to calculate path to airport
*/
private double x_coord_when_called;
private double y_coord_when_called;
private double altitude_when_called;
Calendar calendar = Calendar.getInstance();
/**
* This constructor sets the following fields to random values Dummy Data -
* should have a better way to do this
*/
public Plane() {
rand = new Random();
this.fuelLevelPercentage = rand.nextInt(100);
this.departureAirport = cities[rand.nextInt(cities.length)];
this.passengerCount = rand.nextInt(500);
this.aircraftNumber = rand.nextInt(50000000);
this.airlineCompany = airLineCompanies[rand.nextInt(airLineCompanies.length)];
}
/**
* this fly method will call on a different method depending on the status
* of the Aircraft
*/
public void fly() {
if (status == Status.REGISTERED) {
useFuel();
} else if (status == Status.IN_QUEUE) {
flyInCircle();
useFuel();
} else if (status == Status.LANDING) {
flyToAirport();
useFuel();
} else if (status == Status.LANDED) {
}
}
public void flyInCircle() {
x_coord = FLIGHT_RADIUS * (Math.cos(OMEGA * (time)));
y_coord = FLIGHT_RADIUS * (Math.sin(OMEGA * (time)));
}
public void flyToAirport() {
if (!(x_coord < 1 && x_coord > -1 && y_coord < 1 && y_coord > -1
&& altitude < 1 && altitude > -1)) {
x_coord -= x_coord_when_called * TIME_STEP / TIME_TAKEN_TO_LAND;
y_coord -= y_coord_when_called * TIME_STEP / TIME_TAKEN_TO_LAND;
altitude -= altitude_when_called * TIME_STEP / TIME_TAKEN_TO_LAND;
} else {
System.out.println("Aircraft landed");
status = Status.LANDED;
hour_of_arrival = calendar.get(Calendar.HOUR_OF_DAY);
minute_of_arrival = calendar.get(Calendar.MINUTE);
}
}
/**
* This method changes the flight status to IN_QUEUE - simulates telling the
* plane to join queue
*/
public void directToJoinQueue() {
setFlightStatus(Status.IN_QUEUE);
}
/**
* This method changes the flight status to LANDING - simulates telling the
* plane to land
*/
public void directToflyToAirport() {
setFlightStatus(Status.LANDING);
time_when_called_to_land = time;
x_coord_when_called = x_coord;
y_coord_when_called = y_coord;
altitude_when_called = altitude;
}
/**
* This method reduces fuel level according to fuel usage
*/
private void useFuel() {
if (this.fuelLevelPercentage - TIME_STEP
/ time_taken_to_use_one_percent_of_fuel > 0) {
this.fuelLevelPercentage -= TIME_STEP
/ time_taken_to_use_one_percent_of_fuel;
} else {
this.fuelLevelPercentage = 0;
}
}
/**
* this method sets the flight status
*/
private void setFlightStatus(Status status) {
this.status = status;
}
public double getfuelLevelPercentage() {
return fuelLevelPercentage;
}
public int getPassengerCount() {
return passengerCount;
}
public void setPassengerCount(int passengerCount) {
this.passengerCount = passengerCount;
}
public int getAircraftNumber() {
return aircraftNumber;
}
public String getDepartureAirport() {
return departureAirport;
}
public void stop() {
;
}
public String getAirlineCompany() {
return airlineCompany;
}
public void setAirlineCompany(String airlineCompany) {
this.airlineCompany = airlineCompany;
}
#Override
public String toString() {
if (status == Status.LANDED) {
return String
.format("Flight %-8d | Fuel %-4.1f | Passengers %-3d | From %-10s | %-8s at %d:%d ",
aircraftNumber, fuelLevelPercentage,
passengerCount, departureAirport, status,
hour_of_arrival, minute_of_arrival);
} else {
return String
.format("Flight %-8d | Fuel %-4.1f | Passengers %-3d | From %-10s | %-8s | Coords (%-3.2f,%-3.2f) | Altitude %-4.2f",
aircraftNumber, fuelLevelPercentage,
passengerCount, departureAirport, status, x_coord,
y_coord, altitude);
}
}
public void start() {
aircraftThread = new Thread(this, this.getClass().getName());
aircraftThread.start();
}
#Override
public void run() {
try {
while (true) {
calendar = Calendar.getInstance();
fly();
Thread.sleep(TIME_STEP);
time += TIME_STEP;
}
// System.out.println("aircraft number "+aircraftNumber+" safely landed");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Cohesion is a difficult concept. Despite the other answer's flippant responses, the true answer depends very much on what your system does and how it works. For example lets examine the queue mechanism. In your system, does a plane respond to commands differently when in a queue? If so, then the fact that it is in a queue should be integral to the plane. Does it respond differently when in different queues? If so then the queue itself should be integral to the plane. If, however, it's the airport that responds differently because the plane is in a queue, then the airport should control the queue and the plane should know nothing about it -- it should simply be given a flight path by the airport (or by the control tower at the airport, depending on the resolution of your model).
Cohesion isn't your only problem here. Encapsulation is also a big issue. You are letting other objects have access to your internal state. To model this in a fully OO way, you should consider using the CQRS pattern. If you also consider DDD (Domain Driven Design) techniques, and start by identifying your bounded contexts and aggregate routes, you'll be more likely to derive a correct design.
There's no "standard" for Java or any other language.
I have a "plane" object which stores all the information I need to
sort the plane into a queue and pass to a database. All the vital
information is included in the object but I have also included the
co-ordinates for each plane.
I think your Plane model object is doing too much.
I don't see why it should know whether or not it's in a queue. I'd have a separate object that owns the queue know the rules.
Is queue an in-memory collection or a message queue? Does it matter to you?
Model objects persisting themselves is a point of debate. I think it's easier to separate persistence into a separate data access object so it's easier to test.
Your model might look like this:
package model;
public class Plane {
private int id;
public void save() {
// persist the state of this
// INSERT INTO PLANE(id) VALUES(?)
}
}
I'd have a DAO interface in a separate package:
package persistence;
public interface PlaneDAO {
void save(Plane p);
}
Cohesion can be defined as the degree to which the elements of a module belong together.
Visualizing it helps. Imagine the attributes of a class and the methods. If your class is cohesive, it means the methods are going to use many of the attributes, and conversely, the attributes will be used by many of the methods. This is the "sticking together" notion of cohesion. I like the following visualization that comes from NDepend's placemat:
As others pointed out, the methods that direct the plane (e.g., directToX) are possibly outside of the "theme" of what is a Plane, but they're not flagrantly wrong. Those elements (responsibilities) might be better in another class, say, AirTrafficController. In reality, planes don't decide much how they fly. Their pilots must follow instructions from the ground.
I'd argue that the Thread stuff (start, run) is definitely outside the theme of a Plane. Those methods hardly use anything that are part of a Plane (they are distractions from its theme). You could use an anonymous inner class to handle the processing in a thread from the main and your Plane would be even more reusable (and cohesive).
A cohesive object gets to the essence of the thing it models. This means it could more likely be re-used easily in another application (or even another OO language). Anything that starts to creep outside the true theme of your concept will likely make it harder to re-use the concept in another application. The "distractions" don't make sense anymore in another application.
If you're developing a Kamikaze project (one where you just want to make it work and don't care about re-use), it's perfectly OK to forget about cohesion (and other design elements). Design choices are trade-offs. You could refactor your Plane class to make it more cohesive, but if you never reuse it in another application, you've perhaps wasted your time. On the other hand, design is a learning process; even if you over-design something for one application, you maybe learned something for the next.
Finally, all design aspects are difficult to quantify and therefore there are few standards. Some companies have been known to set (arbitrary) standards for metrics such as LCOM in their development processes. I've read of team standards that say if a class has bad value for LCOM, then it must be refactored until its value goes low enough (its cohesion is stronger). Unfortunately, LCOM can be a bad measure of cohesion (especially in classes that have lots of get/set methods).
There is no java standard regarding object cohesion. (I don't repeat the advices of duffymo, I agree with all of them).
One thing to keep in mind when you elaborate an object model mapping the real world is to try to have one class mapping one concept of the real-world.
As an illustration, in your sample code you have at least 2 distincts concepts : Plane and Flight and so you can split them into 2 separate classes with a one-to-many relationship between them.

Creating weapon classes and a Combat Class

I am creating a text based game and I am having some issues.. This is what I have so far. So far I have a Combat Class, and two Classes for two different Weapons. I am trying to assign hit points to the weapons themselves. But my biggest issue is in the Combat class. I am trying to create it to were there will be random weapon drops at random times and also random Weapons. So far in the Combat class I have this:
public class Combat {
final int chanceOfDrop = 3;
static Weapons[] wepArray = {new M4(), new M16()}
static boolean[] hasWeapon = {false, true};
public static int ranNumberGen(int chanceOfDrop) {
return (int) (Math.random()*1);
}
private void enemyDead() {
boolean canDrop = false;
if(ranNumberGen(chanceOfDrop)==0){
canDrop = true;
}
if(canDrop == true){
givePlayerWeapon(Weapon[Combat.ranNumberGen(Weapons.length)]);
}
private static void givePlayerWeapon(int w) {
hasWeapon[w] = true;
for w <(Weapons.length-1) {
if has weapon[w] {
System.out.println(wepArray[w].getWeaponName);
}
}
}
}
}
}
I have issues when I am creating the new M4(), and the new M16() it says Type mismatch: cannot convert form M4 to Weapons. I do have a class named Weapons, could that be the problem?
And here is my M4 Class, both M4 and M16 Classes are identical
public abstract class M4 {
private Integer weaponDamage = 5;
private Integer weaponAmmo = 25;
private String weaponName = "M4";
public M4(String name, int ammo, int damage) {
name = weaponName;
ammo = weaponAmmo;
damage = weaponDamage;
}
public String getWeaponName() {
return weaponName;
}
public Integer getAmmo() {
return weaponAmmo;
}
public Integer getDamage() {
return weaponDamage;
}
}
I don't think I have any issues here. Maybe my problem lies within this though. Although, I have a Weapons class, but nothing in it. Do I need that?
A few things to fix at first sight:
Create a generic Weapon class that defines some properties that apply to each weapon, like name, damage, ammo, scope multiplier, etc... Then create subclasses for Weapon, like M4 and M16, that specify the properties and eventually add weapon-specific properties.
Add brackets to this line:
System.out.println(wepArray[w].getWeaponName); // Change to getWeaponName()
Remove the abstract keyword from M4.
Fix the ranNumberGen method because it will always return 0 right now. Math.random() returns a float in the range [0,1[. This means that casting it to an int will always result in 0. Multiply it by n to have a random int in the range of [0, n[. You probably want this:
public static int ranNumberGen(int max) {
return (int) (Math.random() * max);
}
Change this line:
givePlayerWeapon(Weapon[Combat.ranNumberGen(Weapons.length)]);
to:
givePlayerWeapon(wepArray[Combat.ranNumberGen(wepArray.length)]);
The syntax of a for-loop is like this:
for (variable-initialization; condition; increment)
So in your case, you want:
for (int i = 0; i < hasWeapon.length; ++i)
{
if (hasWeapon[i]) System.out.println(wepArray[i].getWeaponName());
}
You might want to revisit your decision to use an inheritance-style heirarchy for game objects before it is too late.
In practice, I've found a component-entity model and/or prototype model to be much more effective. You could take a look at the code in my old Java roguelike game Tyrant for inspiration:
Weapon definitions: mikera/tyrant/Weapon.java (Github is down right now so can't find the exact link, but should be easy enough to Google)
The idea is that you make your objects by setting properties / composing compoenents in a Map-like game object rather than using static inheritance.
When you want to create a random weapon in this model, you can just get a list of all the possible weapon prototypes, and clone one of them at random to make a new weapon.
the mean of abstract in "public abstract class M4" is that you cannot make a new object with this class.
So you can put all commons fields of your weapons in the weapon class and make m4 and m16 extends the weapon and you code would compile.

Java Constructors or new class

Hey I am new java so forgive me if what I am about to ask is obvious, but I will try to explain as best as I can.
Its just a project that has been set for university so its not in a serious manner.
I have a class called MarsRoom which holds the attributes say for all the dimensions of the room like the totalheight and width of the walls in order to calculate the heat loss that the room will suffer in order to adjust the amount of solar energy that is needed to keep the room at the room temperature set.
The problem I am having is what is better practice or solution, to pass the attributes of the size of the room in a constructor(but this could get quite long in size, as the ones below are not only the ones that I may need) or create a whole different class specifically for that room like ROOM TYPE U? and set the attributes in there.
As it stands I can create a whole new room just by instantiating the room with the new values, but its going to get a little long, whereas I would rather not create a whole new class for a different room which may only differ from another room by a few meters on one of the walls!.
So what I am really trying to get at it, is is it ok to pass that many attributes to the constructor on instantiation?
//the instantiation in the runnable
MarsRoom room1 = new MarsRoom("RoomU", 40, 40, 20, 20, 8, 2, 4);
//the constructor in the MarsRoom class
public MarsRoom(String roomname, int windowsH, int windowsW, int wallsH, int wallsW, int windowC, int heaters, int lights){
name = roomname;
TotalWindowHeight = windowsH;
TotalWindowWidth = windowsW;
TotalWallHeight = wallsH;
TotalWallWidth = wallsW;
windowCeiling = windowC;
numheaters = heaters;
numlights = lights;
roomheaters = new Heaters[numheaters];
}
I'd say that you should be adding factory methods here.
Basically, keep your constructor, but add methods like
static Room createLaundryRoom(laundryRoomParameters) {
return new Room(...laundry room parameters plus defaults
common to all laundry rooms...);
}
One of the great benefits object oriented programming is the possibility of not repeating yourself in code. Hence objects, which define data (members) and functionality (methods), and no requirement to create instances of these "prototypes" with hard values until run-time. To create a new class for each room when it
may only differ from another room by a few meters on one of the walls
would be to deny OOP (and Java) by gross repetition. I'd stick with the constructors, and if similar kinds of rooms end up emerging, try one of the static factory methods suggested, or break up common functionality using inheritanceOracle.
Create a map with the keys being
Map<String, Integer> map = new HashMap();
map.put("TotalWindowHeight", new Integer(10));
map.put("TotalWindowWidth", new Integer(5));
...
map.put("NumberOfHeaters", new Integer(3));
MarsRoom room1 = new MarsRoom("RoomU", map);
Constructor will be like:
public MarsRoom(String roomname, HashMap<String, Integer> params) {
name = roomname;
TotalWindowHeight = map.get("TotalWindowHeight").intValue();
TotalWindowWidth = map.get("TotalWindowWidth").intValue;
...
roomheaters = new Heaters[map.get("NumberOfHeaters").intValue()];
}
this is not good OO however, but it seems like you are looking for something quick. If you want good OO you need to create an object for Window and in it you have hieght and width, another for ceiling, and you should not have number of something as a field, you should have an array to store the heater objects, and so and so forth, but this is quick and meets your requirement.
While technically legal, constructors with very long argument lists may be inconvenient to use. It also depends on whether you this the list may grow in the future or in subclasses.
If you have many parameters, but they have defaults and sometimes only a few need to be changed, you may find the Builder pattern useful. The idea is to replace constructor arguments with function calls, and allow them to be chained, for example:
public MarsRoom() {
//empty or just basic stuff set here
}
public MarsRoom setTotalWindowHeight(int TotalWindowHeight) {
this.TotalWindowHeight = TotalWindowHeight;
return this;
}
public MarsRoom setTotalWindowWidth(int TotalWindowWidth) {
this.TotalWindowWidth = TotalWindowWidth;
return this;
}
...
then, you can call:
MarsRoom room1 = new MarsRoom()
.setTotalWindowHeight(20)
.setTotalWindowWidth(40);
Of course, if you wanted to set all parameters this way, it's longer (thou maybe more readable) than the single constructor. But if you only set 2 parameters out of 10, it will usually be more convenient.
You don't show what the fields of MarsRoom are, but for each feature, I would have a Collection of sub-objects. A MarsRoom has-a List of Windows. A MarsRoom has-a List of Walls. etc... Then have setters and getters for each and methods to add new instances of these features.
Since this is for school, I'm only including a little bit of pseudo code.
public class MarsWindow {
int height;
int length;
// Setters & Getters
// standard getters & setters go here
int getArea() {
return this.height * this.width;
}
}
public class MarsRoom {
List<MarsWindow> windows;
List<MarsWall> walls;
List<MarsLight> lights;
List<MarsHeater> heaters;
public List<MarsWindow> addWindow(MarsWindow window) {
// Add a window to the "windows" list here
}
public List<MarsWall> addWall(MarsWall wall) {
// Add a wall to the "walls" list here
}
// Do this for the other fields
int getTotalWindowArea() {
int area = 0;
// Iterate over all windows
for(MarsWindow window : windows) {
area += window.getArea();
}
return area;
}
// Add other calculation methods here
}
If what you're trying to do is simply not duplicate the parameters you're passing the constructor, you can simply put them in a separate static method, like so:
public static MarsRoom newRoomU() {
return new MarsRoom("RoomU", 40, 40, 20, 20, 8, 2, 4);
}
You could also use some polymorphism or have different types of rooms or something similar to this and then have a superclass with the common values that all rooms will have.
You can also have more than one constructor and have different ones for values you wish to set depending on the room type etc.
Its always better to work with objects rather than primitives, you could use factory to create objects.
//the constructor in the MarsRoom class
public MarsRoom(String roomname, WindowDimension windowDimension, WallsDimensions wallDimension, RoomAmbience ambience){
}
public class WindowDimension{
private int height; //int windowsH
private int width; //int windowsW
private int circumference; //assumed windowC is circumference
}
public class WallsDimension{
private int height; //int wallsH
private int width; //int wallsW
}
public class RoomAmbience{
private int heaters;
private int lights;
}

Categories