I have something like this:
and I need to know what the best practice is for "hooking up" interactivity.
On the left we have a RoleSlot, which consists of an assigned Employee, and 1-5 ProjectRole(s). If I click on one of those project roles, then we get to view its ProjectResp(onsibilities) on the right.
The question is, how should I do this?
This ProjectManager class and the RoleSlot class are in different packages.
Should I:
Set up a listener to each of those little ListViews from the ProjectManager class? That would require making the RoleSlot's private ListView accessible somehow.
Or should I make the ProjectManager implement the RoleSlotInterface and define a method like handleSelectedRole()?
Or is there another, better choice?
I'd like to know the pros and cons, if possible. Cleaner code is better code.
I would expose a read-only selectedRoleProperty() on the RoleSlot class, and a roleProperty() on the ProjectResp class. I would set up a binding between them in the ProjectManager class.
class RoleSlot {
public ReadOnlyObjectProperty<ProjectRole> selectedRoleProperty();
}
class ProjectResp {
public ObjectProperty<ProjectRole> roleProperty();
}
class ProjectManager {
private final RoleSlot roleSlot;
private final ProjectResp projectResp;
public ProjectManager() {
roleSlot = ...;
projectResp = ...;
projectResp.roleProperty().bind(roleSlot.selectedRoleProperty());
}
}
Related
I'm creating a game with hundreds of abilities, so trying to leverage abstracts and generics as much as possible.
Each ability extends an abstract Ability class with universal methods like getCooldown(player), which gets a specific ability's cooldown for a player. Inheritance saves me from having to duplicate that code in every ability class.
public abstract class Ability {
public static String getCooldown() {
int cooldown;
//logic to get cooldown in milliseconds
return cooldown;
}
}
But the logic and metadata for each ability are unique and coded like such:
public class Parry extends Ability {
public static String getDescription() {
...
}
public static void castAbility() {
...
}
}
Here's my enum. I'm using an enum because abilities and their metadata are constants that are ideally available at compile time. I also don't want to store the metadata separate from the classes which have the rest of the ability logic.
public enum AbilityEnum {
BORN_READY(BornReady.class),
JUGGLER(Juggler.class),
...
PARRY(Parry.class);
public final Class<? extends Ability> cls;
AbilityEnum(Class<? extends Ability> cls) {
this.cls = cls;
}
}
In other parts of the codebase, I want to use the Enum to generically get basic info on an ability, cast a spell, etc. I want to avoid hard-coding for any specific ability because there are 200+ of them. For example, when a player opens their skill menu, I need to grab the descriptions for every ability. I'd rather not type [ability_name].getDescription() 200+ times.
for (AbilityEnum ability : AbilityEnum.values()) {
String tooltip = ability.cls.getDescription();
...
// load descriptions into menu system so players
// can hover abilities for a tooltip description
}
If I try to run this I get the error:
Cannot resolve method 'getDescription' in 'Class'
This confuses me because I bounded the generic, so why does it think it has a Class instead of an Ability? I think I'm either misusing generics or have the wrong syntax for calling methods this way. Perhaps I should be using a list or something else instead of an enum?
I'm creating a RTS game and one of the features is to construct differend kind of buildings. I'm finding a lot of repetition and I was thinking to extract it in helper method, but the problem is that every building is different object which inharits some propertyes from the main building class.
The building methods looks like this:
public static void buildDockyard(Base base) {
if (Validator.checkForBuilding(base, "Dockyard")) {
throw new IllegalStateException("Dockyard is already build");
}
Dockyard dockyard = new Dockyard("Dockyard");
int requiredPower = dockyard.requiredResource("power");
int requiredStardust = dockyard.requiredResource("stardust");
int requiredPopulation = dockyard.requiredResource("population");
Validator.checkResource(base, requiredPower, requiredStardust, requiredPopulation);
updateResourceAfterBuild(base, requiredPower, requiredStardust, requiredPopulation);
dockyard.setCompleteTime(dockyard.requiredResource("time"));
base.getBuildings().add(dockyard);
}
public static void buildHotel(Base base) {
if (Validator.checkForBuilding(base, "Space Hotel")) {
throw new IllegalStateException("Space Hotel is already build");
}
SpaceHotel spaceHotel = new SpaceHotel("Space Hotel");
int requiredPower = spaceHotel.requiredResource("power");
int requiredStardust = spaceHotel.requiredResource("stardust");
int requiredPopulation = spaceHotel.requiredResource("population");
Validator.checkResource(base, requiredPower, requiredStardust, requiredPopulation);
updateResourceAfterBuild(base, requiredPower, requiredStardust, requiredPopulation);
spaceHotel.setCompleteTime(spaceHotel.requiredResource("time"));
base.getBuildings().add(spaceHotel);
base.setCapacity(base.getCapacity() + spaceHotel.getCapacity());
}
I was thinking to refactor like this:
The helper method
private static void construct(Building building, Base base) {
int requiredPower = building.requiredResource("power");
int requiredStardust = building.requiredResource("stardust");
int requiredPopulation = building.requiredResource("population");
Validator.checkResource(base, requiredPower, requiredStardust, requiredPopulation);
updateResourceAfterBuild(base, requiredPower, requiredStardust, requiredPopulation);
building.setCompleteTime(building.requiredResource("time"));
}
Aimed result
public static void buildDockyard(Base base) {
if (Validator.checkForBuilding(base, "Dockyard")) {
throw new IllegalStateException("Dockyard is already build");
}
Dockyard dockyard = new Dockyard("Dockyard");
construct(dockyar, base);
base.getBuildings().add(dockyard);
}
The problem is that each building has unique properties and resource requirements and the main Building class doesn't know about them, so I can't use it as a parameter in the helper method.
All of this is happening in a static helper class for the Base class.
How would you refactor this code ?
Thank you in advance !
Your problems start with using static methods for everything. In an object oriented world you ideally have an object Base and it would have a non-static method addStructure(Struture structure) were Structure is an interface for example. Now you would have objects like Building and Dockyard which would implement Structure.
Implentation of addStructure would be something like this:
if (getBuildings().contains(structure)) {
throw new IllegalStateException(structure.name + " is already build");
}
if (validateStillHaveEnoughResourcesFor(structure)) {
throw new IllegalStateException(structure.name + " can not be added. Not enough resources");
}
getBuildings().add(structure);
Validating structure itself should not be in base. Validating how structure fits to the base should be in the base.
The best way to DRY in Java when making games is to have a clear understanding and terminology of your game. If you read any modern board game manual you will soon see that they will use exactly one word for one concept, like Turn, Round, Building, Player, Resource. This allows to form a rough structure: A Building costs a certain amount of Resource. If a player hasn't enough of Resource then tell him "We need more vespine gas.", etc. The clearer the picture, the DRY-er your Java and easier to create the necessary Classes for your code.
Parameters
If you end up with something like this:
public static void someFunction(Base base, Object param1, Object param2)
public static void someOtherFunc(Base base, Object paramA, Object paramB)
...
Then this is a strong hint that maybe both functions should be part of the Base class.
Enums
If you have a limited set of values then Java Enums can be fantastic to represent them, e.g. your Resource system:
public enum Resource {
POWER, STARDUST, POPULATION
}
Now you don't have to remember if you called it "stardust", "Stardust" or if you even still have a Resource like "stardust". Instead you can use int requiredPower = building.requiredResource(Resource.POWER);
Polymorphism
Let's suppose we have two classes, Building and StarHotel, with StarHotel being a specific kind of Building. Having an abstract class Building allows us to handle some general mechanics in a specific manner, like this:
public abstract class Building {
private ... cost;
private ... requirements;
private ...
// Std-Getter and Setter methods
public ... getCost() { return this.cost; }
}
EVERY Building has a cost, and requirements and other important variables. BUT we handled all the standard stuff of getting and setting these generic variables to a base class from which we now can extend other, more specific buildings. Thanks to the extends keyword you can get the Cost of a StarHotel Object without filling the StarHotel class with repetitive Getters and Setters.
public class StarHotel extends Building {
// Getter, Setter inherited from Building class
}
Interfaces
Java Interfaces allow you to define Interfaces which define methods. In laymen terms: This is useful, because every Class that implements an Interface must implement the method, unless the interface provides the default implementation.
public interface ResourceProvider {
void provideResourceFor(Base base); // A Resource Provider provides Resource for a base.
}
With this interface we have defined that if some Class implements ResourceProvider it has to specify how and what resources to provide for some Base object. Our interface does not care which Resource, which Base and even what provideResourceFor could mean, but as long as something implements ResourceProvider it has to provide the functionality.
Putting all together
Putting Enums, Interface and Polymorphism together, we can now create a StarHotel class that extends Building and implements ResourceProvider, providing 8 Food units and 2 Happiness units to our Base.
public class StarHotel extends Building implements ResourceProvider
public void provideResourceFor(Base base) {
base.addResource(Resource.FOOD, 8);
base.addResource(Resource.HAPPINESS, 2);
}
}
That might be much to take in, but hopefully it will give you a good direction where to look further.
My question might not be useful but I really need to know why a lot of people creat new classes (Right-click, create new, class) but not use code to create a new class (for example: class task(var title:String, var time:String)).
I have never seen creating classes like that. Seems like you've mixed up functions with classes. Can you provide us any source where this way of creating classes is used?
lets says you very simple app with MainActivity.class
you want to add a new class.
you can do it in a separate file (right-click, create a new class) or in the MainActivity.class declare the class, for example
public class Car {
private String carModel;
private String carColor;
//constructor
public Car (String carModel, String carColor) {
this.carColor = carColor;
this.carModel = carModel;
}
//add setters and getters, methods and other stuff
Say I have a class which stores a weapon Arsenal
public class Arsenal{
List<RocketLauncher> rocketLaunchers;
List<HandGrenade> handGrenades;
List<LandMine> landMines;
//Followed by a few more weapons
}
The weapons are part of an enum, Weapon
Now, I am trying to display a summary screen or report elsewhere and I am preparing a map. See the snippet below. I have shown two approaches
public Map<Weapon,Integer> generateReport(Arsenal arsenal){
Map<Weapon,Integer> weaponCountMap = //suitable map impl
//Technique 1
//Create and use a larger num of getters from the Arsenal Class
weaponCountMap.put(Weapon.HAND_GRENADE, arsenal.getHandGrenades);
//.. and so on
//Technique 2
//Create a single method in Arsenal which returns a Map
weaponCountMap = arsenal.getSummary();
return weaponCountMap;
}
Question : Is it just me or does everyone feel 'not right' to use a large number of getters. Suppose Arsenal stores around 50 weapons, it's like 50 methods in the class. Double with setters.
Also. I feel less flexible using the 2nd method, with no accessor methods.
Can you guys please critically evaluate both approaches and possibly suggest new ones?
How about not hard-coding types of weapon inside of your Arsenal? The following is simple implementation of heterogeneous container for your specific case. However, as I don't quite familiar with Generics in enum, this implementation is when you have Weapon and their subclasses, e.g. HardGrenade extends Weapon, etc.
public class Arsenal{
private Map<Class<?>, Collection<?>> weaponArsenal;
public <T extends Weapon> Collection<T> get(Class<T> weaponClass) {
if (weaponArsenal.containsKey(weaponClass) {
return (Collection<T>) weaponArsenal.get(weaponClass);
}
return new ArrayList<T>(); // to avoid checking null everytime in client code
}
public <T extends Weapon> void put(T weapon) {
if (!weaponArsenal.containsKey(weapon.class)) {
Collection<T> weaponList = // initialize correct collection here
weaponArsenal.put(weapon.class, weaponList);
}
weaponArsenal.get(weapon.class).add(weapon);
}
}
and in the client code
Arsenal arsenal = new Arsenal();
arsenal.put(new HandGrenade());
arsenal.put(new RocketLauncher());
Collection<HandGrenade> handGrenades = arsenal.get(HandGrenade.class);
Collection<RocketLauncher> rocketLaunchers = arsenal.get(RocketLauncher.class);
In the arsenal you can duplicate the map instead of using lists. Then in the generateReport method you can iterate over the enum and use the enum value to get the suitable list from the map.
Something like
Arsenal:
Map<Weapon,List<Weapon>> weaponsMap;
arsenalMap.put(Weapon.HAND_GRENADE,handGrenades);
generate report:
for (Weapon weapon: Weapon.values()) {
weaponCountMap.put(Weapon.HAND_GRENADE, arsenal.weaponsMap.get(weapon));
}
Might not be the best solution but you will remove some of the getters.
If you make Arsenal immutable and construct it using a builder (to avoid having a bunch of constructors), you can make the instance variables public.
This approach allows you to use technique 1 from your question but without getters, and still keep state management of the object internal to the object.
public class Arsenal {
public final List<RocketLauncher> rocketLaunchers;
public final List<HandGrenade> handGrenades;
public final List<LandMine> landMines;
//Followed by a few more weapons
private Arsenal(final Arsenal.Builder builder) {
this.rocketLaunchers = Collections.unmodifiableList(builder.rocketLaunchers);
this.handGrenades = Collections.unmodifiableList(builder.handGrenades );
this.landMines= Collections.unmodifiableList(builder.landMines);
// and so on
}
public static class Builder {
private final List<RocketLauncher> rocketLaunchers = new ArrayList<>();
private final List<HandGrenade> handGrenades = new ArrayList<>();
private final List<LandMine> landMines = new ArrayList<>();
public Builder rocketLaunchers(List<RocketLauncher> rocketLaunchers) {
this.rocketLaunchers.addAll(rocketLaunchers);
return this;
}
public Builder handGrenades(List<HandGrenade> handGrenades) {
this.handGrenades.addAll(handGrenades);
return this;
}
public Builder landMines (List<LandMines> landMines ) {
this.landMines .addAll(landMines );
return this;
}
public Arsenal build() {
return new Arsenal(this);
}
}
}
You can now use this in the following way.
List<RocketLauncher> rocketLaunchers = //whatever
Arsenal arsenal = new Arsenal.Builder().rocketLaunchers(rocketLaunchers).build();
....
weaponCountMap.put(Weapon.HAND_GRENADE, arsenal.handGrenades);
//.. and so on
All fields of the arsenal are non-null, and can't be modified. If you define the builder in an external class (i.e. not a static class within Arsenal) your Arsenal class will be very small - just fields and the constructor, plus logic methods.
Please have a look at other posts like this one...Java Conventions: use getters/setters WITHIN the class? to obtain an idea on when you could use getters instead of direct access.
Its kind of a design question depending upon what you would like to encapsulate within your arsenal class and what you would like to expose to the outside world for viewing your arsenal.
Its like the UN trying to inspect your arsenal and you tell them hey I wont tell you what weapons I am dealing with in my arsenal but I can give you a report and that is the report you externalize to the outside world. Now it depends on you what report you want to give out and which weapons will land in that map of yours.
Now looking at your second technique, are you planning to shift the map creation logic into your Arsenal class.
Another design question to answer .... is this logic just to obtain reports then my suggestion is to keep it out of the arsenal class and keep it light.
Otherwise you might end up putting in all kind of report logics into the arsenal class and it will get heavier and might explode.
See https://projectlombok.org/ or https://github.com/google/auto/tree/master/value.
Add separate field for each weapon, Lombok or AutoValue will create getters/setters/hashcode/equals/toString methods for you.
The main objective is to avoid duplication of fields with best inheritance model on existing stuff:
I have following class hierarchy:
class A
{
private String fieldA1;
private String fieldA2
// getters and setters
}
Class B extends A
{
private String fieldB1;
private String fieldB2;
// getters and setters
}
A 'User' class will either extend A or B having its own fields.
Assume existing API's uses above data models.
Now there is a requirement to add new fields in Class A for new set of API's.
I have certain restriction not to modify existing data models since it exposes or breaks existing API's with these new fields . So new data models need to be created by extending existing ones such that new API's uses them with all existing fields + new fields. I have below solution which may not be the best approach as it adds duplication of fields.
class A
{
private String fieldA1;
private String fieldA2
// getters and setters
}
Class B extends A implements Common
{
private String fieldB1;
private String fieldB2;
// getters and setters
}
Class NewA extends A
{
private String fieldNewA;
// getters and setters
}
Class NewB extends NewA implements Common
{
private String fieldB1;
private String fieldB2;
private String fieldNewB;
// getters and setters
}
Interface Common
{
//marker interface
}
Please suggest if there is any better approach.
Thanks...
I think you need to take a step back and look at what is it that is causing you to keep adding new fields to your existing classes. Maybe you should just have one field which is a collection of property and values. This way any new field will just be another property/value in your collection. You won't have to then worry about adding new fields frequently.
Your example is not great, for example sub-classes cannot access private fields and it is not clear what you intend your subclasses to achieve.
If you wish to inherit state, your example should use either protected or public fields, or show getter and setter methods being inherited. When inheriting state, implementing an interface like Common is redundant. More detail may help clarify your intent.
That aside, yes you can extend your data models as you describe in order to add data types to an existing class. An example of wanting to do this might be wishing to create a Square class from a Triangle class, requiring adding a new point. And also requiring changing behaviour to make use of the new field.