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

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. :-)

Related

Use of static classes to represent different forms of currency

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()];
}
}

How do I separate the choices in a list

The purpose of the program is to calculate the volumes of different geometrical figures (Like a cylinder or a pyramid). I've started out by adding a list where the user can choose between the different figures.
The problem is that I don't know how to make the program know which formula to use. I need to be able to separate the choices instead of just making an int out of the answer.
private void btnAktiveraActionPerformed(java.awt.event.ActionEvent evt) {
String form = listForm.getSelectedValue().toString();
int fo = Integer.valueOf( form );
String höjd = txfHöjd.getText().toString();
int hö = Integer.valueOf( höjd );
String bredd = txfBredd.getText().toString();
int br = Integer.valueOf( bredd );
String radie = txfRadie.getText();
int ra = Integer.valueOf(radie);
String djup = txfDjup.getText();
int dj = Integer.valueOf(djup);
double ACyl = 3.14*ra*ra*hö;
double APyr = (br*dj*hö)/2;
double AKub = br*dj*hö;
double ARät = br*dj*hö;
txfHöjd.setEnabled(false);
txfBredd.setEnabled(false);
txfDjup.setEnabled(false);
txfRadie.setEnabled(false);
listForm.setEnabled(false);
}
private void btnBeräknaActionPerformed(java.awt.event.ActionEvent evt) {
// I know this code won't work, its just a reminder.
if (answer == Cyinder){
System.out.print("volymen är: "+ACyl+" cm^3");
}
}
I don't understand your question very clearly. I would suggest to make a plan to solve your problems.
make a list of figures that program will calculate
make a list of methods to count volumes of those figures
create individual classes, variables etc...
create methods
create main method with user input
You mentioned you don't know which formula to use. I assume there won't be many formulas in your program. I would create an individual method for each individual figure i.e. piramidFormula(), cilinderFormula()...
There is no point to refer to polimorphism when I think your level of programming is very basic at this stage.
I hope that will help you a little bit.
You need a list to hold the things, you seem to understand this just fine.
You need a way to select things. Selection is typically not exactly the same thing as the list, you need a class to be responsible for the "selection" behaviour.
Each thing has a routine that can calculate the volume. That means it will need input parameters. This is where it starts to get tricky, because if you want all of your things to be in the same list, you need to decide how to manage the different input parameters for the different types in the list.
public List<VolumeCalculations> volumeCalculations ...
public interface VolumeCalculation {
public double getVolume();
}
public class CubleCalcuation implements VolumeCalculation {
private double side = 0;
public void setSide(double value) {
this.side = value;
}
#Override
public double getVolume() {
return side*side*side;
}
}
the other volume calculations are left as an exercise to you.
Then you need to put them all in the list
volumeCalculations.add(new CubeVolumeCalculation());
...
But when you select the calculation, you will need "something" to ask for the right input.
public interface CalculationInputGather {
public void setCalcualtion(VolumeCalcuation value);
public void askForInputs();
}
which the one for the CubleCalcuation might look like
public CubeInputGather implements CalculationInputGatherer {
#Override
public void setCalculation(VolumeCalcualtion value) {
if (value instanceof CubeCalcuation) {
this.volume = value;
}
throw new IllegalArgumentException("value must be a CubeCalculation");
}
public void askForInputs() {
System.out.println("enter the side value:");
// read the value
volume.setSide(value);
}
}
then when you know the selected item in the list, you can use a Map of Calcuations to their input gatherers to lookup the right input gatherer for the selected calcuation.
If you already have the list for the user to choose from, maybe consider a map instead. You can have all your shapes as the keys of the map and then the formulas for volume as the values of the map. The list of shapes can be provided to the user via the keySet and their response can be matched back against the map to find the formula.
EDIT: You have your formulas for each shape inside an action event. You'll need to move those into a separate class
public static class Formulas() {
// list all formulas here
private String cylinder = "3.14*r*r*h";
}
Then when you hit the action you can either create a new instance of the Formulas class and use any convenience methods you might write in there.

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 Array of weapons for Math.Random to choose from

I am just a little bit confused as of what to do. I have Two Weapons Classes. One for the M16 and another for the M4. I then have those Classes implementing an interface named Armory. But I am having issues with the Combat class. In the combat class I have a Random number Generator that will generate a random number and depending on what number it is, will either give the player a weapon or do nothing. I will post the Code Below:
Interface:
public interface Armory {
public Integer weaponAmmo(int wepAmmo);
public Integer weaponDamage(int wepDamage);
public String weaponName(String wepName);
}
M4 Class(M4 and M16 Classes are the same except for damage and ammo amounts):
public class M4 implements Armory {
public Integer weaponAmmo(int wepAmmo) {
wepAmmo = 10;
return wepAmmo;
}
public Integer weaponDamage(int wepDamage) {
wepDamage = 2;
return wepDamage;
}
public String weaponName(String wepName) {
wepName = "M4";
return wepName;
}
And Finally, the Combat Class(This is where I am having Issues):
public class Combat {
final int chanceOfDrop = 3;
Weapons[] wepArray = {new M4(), new M16()}; //Issues here.. Don't really know how to implement this.
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(wepArray[Combat.ranNumberGen(wepArray.length)] } //Issues here also.
private static void givePlayerWeapon(int w) {
hasWeapon[w] = true;
for (int i = 0; i < hasWeapon.length; ++i)
{
if (hasWeapon[i]) System.out.println(( wepArray[i]).weaponName()); //And, last but not least, I am having Issues here
}
}
}
NOTE: I have a Weapons Class, But nothing is in it. I don't really know what to put in it.
Any Suggestions?
Thanks in advance:
Shandan
Several issues -
A. To put m16 and m14 elements in the weapons array , these classes must either extend (if Weapons is a class) or implmeent (if weapons is interface) Weapons.
Another option is to have a method of
Weapons toWeapons() in both M16 and M14 classes.
B. Correct me if I'm wrong (Not native english speaker - but Armory is a place that provides Weapons, so your choice of name is not good.
M16 and M14 should implement an interface named "Weapon" and this (In my humble opinion) should be the type of the array.
C. If I understood, you want to provide in some cases no weapon to the user -
One way to have this done, and not get into ugly if (to check existence or not) is to have a NoWeapon class implements Weapon (in your current code - implements Armory).
Its methods will have an applicative meaning of "do nothing".
For example -
weaponAmmo will always return 0.

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.

Categories