I am attempting to create an inventory tracking system. I have a class (in Java) called "InventoryItem" with the properties of name and quantity.
This works fine for simple objects, but what if my inventory item contains other inventory items, for example, a server with RAM?
Should I be creating my own datatype, or is there a better way to do this (linked listed maybe)? should my class extend whatever that datatype is or should I not bother creating my own class?
My class so far:
public class InventoryItem {
private String name;
private int quantity;
private InventoryItem childInventoryItem;
// CONSTRUCTORS
public InventoryItem() {
}
public InventoryItem(int quantity, String name) {
this.quantity = quantity;
this.name = name;
}
//GETTERS
public String getName() {
return name;
}
public int getQuantity() {
return quantity;
}
//SETTERS
public void setName(String name) {
this.name = name;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
A tree is usually what is involved in any parent-child relationship. If you aren't doing anything complicated, you can simply maintain an internal list that is basically List<InventoryItem> which contains any child items.
So all you would add to your class is something like this:
public class InventoryItem {
...
private List<InventoryItem> composingItems = new ArrayList<>(); //if still using Java 6 this must be new ArrayList<InventoryItem>();
...
public void addComposingItem(InventoryItem composingItem) {
composingItems.add(composingItems);
}
public List<InventoryItem> getComposingItems() {
//Enforce immutability so no one can mess with the collection. However
//this doesn't guarantee immutability for the objects inside the list;
//you will have to enforce that yourself.
return Collections.umodifiableList(composingItems);
}
}
There are many ways you can do this. I think the easiest way would be to create an array list.
ArrayList<InventoryItem> childInventory = new ArrayList<InventoryItem>();
Then create a setter that adds inventory items to this array
public void addChildItem(InventoryItem child)
childInventory.add(child);
This way you would have a list of all of the child items within the item. You could also make a method to return a list of all of the child items in either an array or an ArrayList.
public ArrayList<InventoryItem> getChildItems()
return childInventory;
Related
I have a class called SalesOrder (SO), that allows users to buy several items in a single order. SO has an order number.
class SalesOrder {
public String orderNumber;
}
Each SO has many items in it, so I have created a new class OrderItem which has the item name and price.
class OrderItem {
public String name;
public double price;
}
Each SO has a order header, include user name and address. It also has a field called total price, which hold the sum of all items prices
class OrderHeader {
public String username;
public String address;
public double totalPrice;
}
After that, I added two fields to SO:
class SalesOrder {
...
public List<OrderItem> items;
public OrderHeader header;
}
Because OrderItem and OrderHeader are always used with SalesOrder and the header should return all items prices, I converted them to be be inner classes of SalesOrder.
class SalesOrder {
...
public SalesOrder() {
this.items = new ArrayList<>();
this.header = new OrderHeader();
}
public class OrderItem {
...
}
public class OrderHeader {
...
public double getTotalPrice() {
double total = 0.0;
// loop SalesOrder.items
total += items[i].price;
return total;
}
}
}
My question is whether using inner classes like this is good OOP design? If not, how should they be designed?
======= Update Some information =======
I'm very sorry that I haven't give more inforamtion.
Header and Item make they construe method private, other object can't create them without SalesOrder.
SalesOrder have a factory method
class SalesOrder {
...
public SalesOrder parseOrder(Xml xml) {
//init header and items from xml
this.header = new OrderHeader(valueFromXml, valueFromXml);
}
public class OrderHeader {
....
private OrderHeader(username, address) { ... }
}
public Class OrderItem {
...
private OrderItem(name, price) { ... }
}
}
And other object use them like this
Xml xml = orderXmlData;
SalesOrder order = SalesOrder.parseOrder(orderXmlData);
OrderItem item = order.item;
OrderHeader header = order.header;
There are a few suggestion I would have that might improve your design. Firstly, it seems unlikely to me that the totalPrice should be part of the header. It seems more likely that it is derived from the order items rather than being a component of the header. Secondly, unless you want clients of the class to create order items independent of the order then there seems no need to define them as a class. Better to convert to an interface returned from the order. Thirdly, there's no reason why Header can't be interface - this allows a client to use any class they want as a header as long as it has name and address.
So my suggestion would be something like:
class Order {
interface Item {
String getName();
double getPrice();
}
interface Header {
String getName();
Address getAddress();
}
public Order(Header header) {
...
}
public double getTotalPrice() {
return streamItems().mapToDouble(Item::getPrice).sum();
}
public void addItem(String name, double price) {
...
}
public Stream<Item> streamItems() {
...
}
}
The use of nested classes depends on the requirements. I do not see any need for the use of nested classes in your code. If it is a Java Beans class, then you should keep the classes separated so they can be reusable. Other than your last block of code with the nested classes, your design is perfect.
I am making an inventory system.
I want to ensure that objects I am creating (Ingredients) all have unique names. In other words, I want to make sure that there are never two Ingredients that have the same name in the whole program. Currently I have the following class:
package ingredient;
import java.util.HashSet;
public class Ingredient {
private final String name;
private final double price;
private static HashSet<String> names = new HashSet<String> ();
private Ingredient(String ingr_name, double ingr_price) {
name = ingr_name;
price = ingr_price;
}
public static Ingredient createIngredient(String ingr_name, double ingr_price) {
if (names.contains(ingr_name)) {
return null;
} else {
names.add(ingr_name);
return new Ingredient(ingr_name, ingr_price);
}
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
Then, when I go to actually make new ingredients, I make statements such as :
Ingredient egg = Ingredient.createIngredient("egg", 1);
Is this okay design? I suppose I am concerned because returning "NULL" might not be the best practice here.
I cant comment, but whatever...
I would go about this by storing all of the Ingredients in a different class, then you wouldn't need all this static nonsense. In the class where you actually create a new Ingredient (Ingredient egg = Ingredient.createIngredient("egg", 1);) you could maybe create an ArrayList of ingredients like so:
ArrayList<Ingredient> ingredients = new ArrayList<>();
Then when you make a new Ingredient you would just have to make sure you add it to the ArrayListand when you do so, check that none of the ingredients are already there, maybe something like this:
createIngredient("egg", 1);
or
Ingredient egg = createIngredient("egg", 1);
...
private Ingredient createIngredient(String ingr_name, double ingr_price){
for(Ingredient i : ingredients){
if(i.getName().equals(ingr_name)){
return null;
}
}
Ingredient newing = new Ingredient(ingr_name, ingr_price);
ingredients.add(newing);
return newing;
}
Then the Ingredient class could be cut down to something like this:
package ingredient;
public class Ingredient {
private final String name;
private final double price;
public Ingredient(String ingr_name, double ingr_price) {
name = ingr_name;
price = ingr_price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
And then you could access each individual Ingredientwith a method to run through the ArrayList and find the Ingredient with the name your looking for:
public Ingredient findIngredient(String name){
for(Ingredient i : ingredients){
if(i.getName().equals(name)){
return i;
}
}
return null;
}
I would recommend either
A) returning the already created ingredient
Or if that would confuse the caller,
B) throwing an exception
This can be a simple IllegalArgumentsException, or depending on your needs, a custom exception class.
I am trying to ensure that the objects i insert in productDatabase have not been already inserted and that i sort my arraylist using method sortData but without using any comparators in method sortData
public class Application {
public static void main(String[] args) {
Product p = new Product(15,"test",3.45);
Product p2 = new Product(15,"test",3.45);
Product p3 = new Product(4716,"koukouroukou",1.25);
Product p4 = new Product(6002,"bananofatsoula",0.60);
ProductDatabase productDatabase = new ProductDatabase();
productDatabase.addProduct(p);
productDatabase.addProduct(p2);
productDatabase.addProduct(p3);
productDatabase.addProduct(p4);
productDatabase.printDatabase();
productDatabase.sortDatabase();
productDatabase.printDatabase();
}
public class Product {
private int code;
private String name;
private double price;
public Product(int code, String name, double price){
this.code = code;
this.name = name;
this.price = price;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String toString(){
return code+" , description: "+name+", price: "+price;
}
public int hashCode(){
return 31 * code + name.hashCode();
}
public boolean equals(Object o){
Product other = (Product)o;
if (this.code == other.code){
return true;
}
else{
return false;
}
}
import java.util.ArrayList;
import java.util.Collection;
public class ProductDatabase {
private ArrayList<Product> productDatabase;
public ProductDatabase(){
productDatabase = new ArrayList<Product>();
}
public void addProduct(Product p){
if(!productDatabase.contains(p)){
productDatabase.add(p);
}
}
public void printDatabase(){
for(Product product : productDatabase){
System.out.println(product);
}
}
public void sortDatabase(){
// ????????????????????????????????????????????????????????????
So my questions are
Does contain(p) is enough to ensure that the same product is not already in the list?
products are the same when they have the same code and name.if not what i have to do?
How i sort my withous using comparators in class ProductDatabase.maybe by a new method in product ?
Does productDatabase extends Product???
You can return bool value in order to know the product is already there or not . Code is used to ensure the differentiation of products if not add another code id.
Product instance will carry information of just one Product so Sort must be done in the member function of class having all the records of product, not just one.
No product_database does not extend product . It is a log of product class not a part .
your questions 1 and 2 are a little unclear. Can you re-write them? As for question 3. No ProductDatabase does not extend product, neither should it. ProductDatabase HAS products. ProductDatabase is not a product
Yes, contains() is enough (in our case) to ensure uniqueness
Since you implemented equals and hashCode - you're good
You don't need to sort if you don't have another purpose to do so, but since you're using an ArrayList every time contains() is called it iterates the whole list which is not very efficient. A better implementation would use Set (a HashSet for example)
ProductDatabase does not have to extend Product - it contains a list/set of products but it doesn't have any character/behavior like Product
Yes, contain(p) is enough to ensure that the same product is not already in the list, because you overrided "equals" method.
In "equals" you can use shorter construction:
Product other = (Product)o;
return this.code == other.code;
For sort ArrayList with java.util.Collections class two options possible:
Collections.sort(List list, Comparator c). You have to write own Comparator class and pass as second parameter.
Collections.sort(List list) and class Product must implement Comparable interface
Yes, Contain(p) is enough to ensure that the same product is not already in the list BUT that is NOT efficient. Use a Set instead of ArrayList.
For question 2, you have to decide the when the two products are equal and code that in your equals method like you did for 'product code'
So, I have this class:
public class Product {
private String name, id, info ;
private int quantity;
public Product(String newName, String newID, String newInfo, Integer newQuantity){
setName(newName);
setID(newID);
setPrice(newInfo);
setQuantity(newQuantity);}
public void setName(String name) {
this.name = name; }
public void setID(String id) {
this.id = id; }
public void setPrice(String info) {
this.info = info; }
public void setQuantity(Integer quantity) {
this.quantity = quantity; }
public String getID( ) {
return id; }
public String getName( ) {
return name; }
public String getInfo( ) {
return info; }
public int getQuantity( ) {
return quantity; }
In another class i have this:
public class Invoice implements Group<Product> {
private HashMap<String, Product> prod = new HashMap<String, Product>( );
public Invoice(){ }
public void addProd(Product a) {
prod.put(??getID()??,new Product(??));
}
}
If this data was user generated rather than me, I would use the getID() method right?
So in my class invoice, how do i use the method getID(), so that I can use it in the parameter for my key value in the HashMap? Also is there a way to add 3 values (name info quan) to the hashmap without making a new class?
I see that you get Product object with ref "a" as parameter to your addProd method.
And you can get id by just using a.getID(). It should look as:
public void addProd(Product a) {
prod.put(a.getID(),a);
}
I didn't understand second part of your question.. I think you already have 3 values in your Product object and you put Product object to Map, So why do you require another way ?
Your class Product does not compile, because you have the name Item in your constructor. The constructor name must match the class name. So change that to Product. The same applies to Invoice vs ShoppingCart. Constructor and Class names must match.
As per your comment, you'd like to add four product values to a Map. The key being one of the values of the product itself. Try this:
Product p = new Product(name, id, info, quantity);
cart.addProd(p);
...
public void addProd(Product p) {
prod.put(p.getId(), p);
}
Maps can only map a single value to a single key, so you must have some sort of container for the values you wish to collate into one value. This can be an object (Product) or you could use a collection (e.g. List). I strongly recommend the former.
For your question about putting 3 values in your map, I don't think there's a way for you to put 3 values into one key without creating a class. An alternative is to store a Map<String, List<String>> assuming your 3 values are type String, or, Map<String, Map<String, String>>.
I have situation. I have to create a Sports Club system in JAVA. There should be a class your for keeping track of club name, president name and braches the club has. For each sports branch also there should be a class for keeping track of a list of players. Also each player should have a name, number, position and salary.
So, I come up with this. Three seperate classes:
public class Team
{
String clubName;
String preName;
Branch []branches;
}
public class Branch
{
Player[] players;
}
public class Player
{
String name;
String pos;
int salary;
int number;
}
The problems are creating Branch[] in another class and same for the Player[]. Is there any simplier thing to do this? For example, I want to add info for only the club name, president name and branches of the club, in this situation, won't i have to enter players,names,salaries etc. since they are nested in each other. I hope i could be clear. For further questions you can ask.
Here's a more complete, formal example of your scenario using conventional Accessors/Mutators (getters/setters), constructors, and Lists. The main() method below illustrates how to use your classes.
public class SportsClub
{
public static void main(String[] args)
{
//Create a team without any branches
Team myTeam = new Team("Southpaws", "South");
//Create a new Branch without any players
Branch myBranch = new Branch();
//Add myBranch to myTeam
myTeam.getBranches().add(myBranch);
//Create a new player
Player myPlayer = new Player("Bob", "Center", 120, 3);
//Add myPlayer to myBranch (and therefore myTeam)
myBranch.getPlayers().add(player);
}
}
public class Team
{
private String clubName;
private String preName;
private List<Branch> branches;
public Team(String clubName, String preName)
{
this.clubName = clubName;
this.preName = preName;
branches = new ArrayList<Branch>();
}
public String getClubName() { return clubName; }
public String getPreName() { return preName; }
public List<Branch> getBranches() { return branches; }
public void setClubName(String clubName) { this.clubName = clubName; }
public void setPreName(String preName) { this.preName = preName; }
public void setBranches(List<Branch> branches) { this.branches = branches; }
}
public class Branch
{
private List<Player> players = new ArrayList<Player>();
public Branch() {}
public List<Player> getPlayers() { return players; }
public void setPlayers(List<Player> players) { this.players = players; }
}
public class Player
{
private String name;
private String pos;
private Integer salary;
private Integer number;
public Player(String name, String pos, Integer salary, Integer number)
{
this.name = name;
this.pos = pos;
this.salary = salary;
this.number = number;
}
public String getName() { return name; }
public String getPos() { return pos; }
public Integer getSalary() { return salary; }
public Integer getNumber() { return number; }
public void setName(String name) { this.name = name; }
public void setPos(String pos) { this.pos = pos; }
public void setSalary(Integer salary) { this.salary = salary; }
public void setNumber(Integer number) { this.number = number; }
}
To answer your question, yes, you can create these objects without populating the Lists with players. The SportsClub.main() above illustrates that.
I would use a List rather than an array since they're (easily) dynamically resizable, but otherwise, you're on the right track.
Think about encapsulation and visibility too. Make all those fields private and provide accessors.
You could create an empty Branch[] array (or better yet - a list) at initialization and add to it later, that way you don't have to enter all the information upon creation - same goes for Player[].
Something like:
public class Team
{
String clubName;
String preName;
private List<Branch> branches;
public Team (String club, String pre) {
clubName = club;
preName = pre;
branches = new LinkedList<Branch>();
}
public void addBranch (Branch branch) {..}
}
public class Branch
{
private List<Player> players;
public Branch () {
players = new LinkedList<Player>();
}
public void addPlayer (Player player) {..}
}
public class Player
{
String name;
String pos;
int salary;
int number;
}
I think that's good. You should probably have methods in the classes to manage your information though--don't try to do anything serious from "Outside" these classes.
to be more specific: All your members should be private and only used/accessed from within the classes--also in general avoid setters and getters, instead ask the class to do things for you.
For example, if you wanted to know how many players were in a branch, you would call branch.countPlayers, not access the Player array to count the players from outside.
If you wanted to know how many players were in a team, you would call team.countPlayers which would call branch.countPlayers for each Branch, sum them up and return the value.
If you wanted to see which branch a player was in, you would call Team.findPlayer(playerName). Team would call branch.hasPlayer(playerName) on each branch until it returned a true, then Team would return the Branch object that returned true.
etc.
Note that this resolves your "Populated or not" issue. If you simply have methods like "hasBranch()", "addBranch()", "removeBranch()" then it doesn't matter how or when you populate the branches array since you control it all within the Team class you can change it's implementation at any time and not change a single line outside that class.
You won't have to enter anything into the players array, nor the branch[]. Provided you make the fields accessible, of have properties, you will be able to put them in however you like.
The class structure looks good to me, but a List would be better so that you don't have to worry about resizing arrays down the road.
Nothing wrong with your classes. I personally would use a strongly-typed List to store the branches and players:
public class Team
{
String clubName;
String preName;
List<Branch> branches;
}
public class Branch
{
List<Player> players;
}
Not sure of the requirement, but you'd probably want to have some kind of identifier or name for each Branch, right?
There's nothing in these classes that forces you to create new players just to instantiate a Branch. The list of Players can remain null or empty until you need them.