//Represents list books command for biblioteca
public class ListBooksCommand implements Command {
private static final String BOOKS = "Books::";
private static final String FORMAT = "%-35s %-35s %-35s";
private static final String HEADER = String.format(FORMAT, "Name", "Author", "YearPublished");
private static final String NO_BOOKS_AVAILABLE = "No Books Available";
private final Biblioteca biblioteca;
private final IO io;
public ListBooksCommand(Biblioteca biblioteca, IO io) {
this.biblioteca = biblioteca;
this.io = io;
}
#Override
public void execute() {
if (this.biblioteca.isEmpty(Book.class)) {
this.io.println(NO_BOOKS_AVAILABLE);
return;
}
this.displayBooks();
}
private void displayBooks() {
this.io.println(BOOKS);
this.io.println(HEADER);
this.io.println(this.biblioteca.representationOfAllLibraryItems(Book.class));
}
}
public class ListMoviesCommand implements Command {
private static final String Movies = "Movies::";
private static final String FORMAT = "%-35s %-35s %-35s";
private static final String HEADER = String.format(FORMAT, "Name", "Director", "YearPublished");
private static final String NO_BOOKS_AVAILABLE = "No Movies Available";
private final Biblioteca biblioteca;
private final IO io;
public ListBooksCommand(Biblioteca biblioteca, IO io) {
this.biblioteca = biblioteca;
this.io = io;
}
#Override
public void execute() {
if (this.biblioteca.isEmpty(Movie.class)) {
this.io.println(NO_MOVIES_AVAILABLE);
return;
}
this.displayMovies();
}
private void displayMovies() {
this.io.println(MOVIES);
this.io.println(HEADER);
this.io.println(this.biblioteca.representationOfAllLibraryItems(MOVIE.class));
}
}
I have two classes here one is listbooks command , listmovies command both acts on biblioteca. Both Book and Movie is of type LibraryItem(interface).
Both below codes are same. Both will ask biblioteca to get the representation of its own type. And both commands will display the representation.
This is biblioteca implementation
//Represents a library
public class Biblioteca {
private final List<LibraryItem> allLibraryItems;
public String representationOfAllLibraryItems(Class<? extends LibraryItem> itemType) {
return this.allLibraryItems
.stream()
.filter(libraryItem -> libraryItem.getClass().equals(itemType))
.map(LibraryItem::representation)
.collect(Collectors.joining(LINE_SEPARATOR));
}
public boolean isEmpty(Class<? extends LibraryItem> itemType) {
return this.allLibraryItems.stream().noneMatch(libraryItem -> libraryItem.getClass().equals(itemType));
}
}
Please suggest me a pattern to avoid duplication.
Note: I'm not aware about your requirements. I'm just proposing some general design observations in this answer.
Observation 1: Biblioteca being a library, has library items. In your case, the items in the library are Movie items and Book items. So the library has two main types of items (or it can even contain more. Doesn't matter). Hence the member of Biblioteca should be:
private HashMap<Class<? extends LibraryItem>, List<LibraryItem>> libraryItems;
A map that has item type as Key and List<LibraryItem> as value.
Biblioteca should also contain querying methods that will return the representations for a given item type and representations for all item types. So in my view, Biblioteca class should look like this:
public class Biblioteca {
private HashMap<Class<? extends LibraryItem>, List<LibraryItem>> libraryItems;
public Biblioteca(HashMap<Class<? extends LibraryItem>, List<LibraryItem>> libraryItems) {
this.libraryItems = libraryItems;
}
/*
* Representation of a given type
*/
public String representationOfLibraryItemType(Class<? extends LibraryItem> itemType) {
if(libraryItems.containsKey(itemType)) {
return libraryItems.get(itemType).stream()
.filter(libraryItem -> libraryItem.getClass().equals(itemType))
.map(LibraryItem::representation)
.collect(Collectors.joining(System.lineSeparator()));
} else {
throw new IllegalArgumentException("Missing type " + itemType.getSimpleName());
}
}
/*
* Representation of all types
*/
public List<String> representationOfAllLibraryItems() {
return libraryItems.values()
.stream()
.flatMap(list -> list.stream()
.map(LibraryItem::representation))
.collect(Collectors.toList());
}
}
The method representationOfLibraryItemType should be taking in a Class of item type for filtering. If the item type is found in the library, return it's representations or else throw an exception saying it's an unknown item type.
On the other hand, representationOfAllLibraryItems() should not take any input parameters. It should return all the available representations in the library.
Observation 2: Your LibraryItem should be an abstract class and each of the items in your library should extend this particular class. Because Movie is-a LibraryItem and Book is-a LibraryItem. Now, each of your items can override representation() method which is an abstract method in LibraryItem. Your LibraryItem class should look something like this:
public abstract class LibraryItem {
abstract String representation();
}
Observation 3: Your Book and Movie classes should be independent of Biblioteca because they are just items in-a Library. Today they are in a library called Biblioteca and tomorrow they can be in a library called CentralHallLibrary. So, your item class should be looking something like this:
/*
* Book Item
*/
public class Book extends LibraryItem {
private String title;
private String author;
private String publishedYear;
public Book(String title, String author, String publishedYear) {
this.title = title;
this.author = author;
this.publishedYear = publishedYear;
}
#Override
public String representation() {
/*
* I'm just returning a call to toString
* from this method. You can replace it
* with your representation logic.
*/
return toString();
}
#Override
public String toString() {
return "Book [title=" + title + ", author=" + author + ", publishedYear=" + publishedYear + "]";
}
}
/*
* Movie Item
*/
public class Movie extends LibraryItem {
private String title;
private String director;
private String releaseYear;
public Movie(String title, String director, String releaseYear) {
this.title = title;
this.director = director;
this.releaseYear = releaseYear;
}
#Override
public String representation() {
/*
* I'm just returning a call to toString
* from this method. You can replace it
* with your representation logic.
*/
return toString();
}
#Override
public String toString() {
return "Movie [title=" + title + ", director=" + director + ", releaseYear=" + releaseYear + "]";
}
}
Observation 4: I didn't find any use of the Command class which you are using. Because, as I see, your Command class has only one method called execute() that is used for displaying the representations. Generally I would put such "displaying" code in my client side (UI). If Command class has no other functions other than only printing stuff, it's not necessary in my opinion.
Testing the design: Let's create few Book items and few Movie items and then add those to the Biblioteca library
Book effJava = new Book("Effective Java", "Josh Bloch", "2008");
Book cloudNativeJava = new Book("Cloud Native Java", "Josh Long", "2017");
Book java9modularity = new Book("Java 9 Modularity", "Paul Bakker", "2017");
Movie gotgV2 = new Movie("Guardians of the Galaxy Vol. 2", "James Gunn", "2017");
Movie wonderWoman = new Movie("Wonder Woman", "Patty Jenkins", "2017");
Movie spiderHomeCmg = new Movie("Spider-man Homecoming", "Jon Watts", "2017");
List<LibraryItem> bookItems = new ArrayList<>();
List<LibraryItem> movieItems = new ArrayList<>();
bookItems.add(java9modularity);
movieItems.add(spiderHomeCmg);
bookItems.add(cloudNativeJava);
movieItems.add(wonderWoman);
bookItems.add(effJava);
movieItems.add(gotgV2);
HashMap<Class<? extends LibraryItem>, List<LibraryItem>> store = new HashMap<>();
store.put(Movie.class, movieItems);
store.put(Book.class, bookItems);
//CREATE STORE
Biblioteca bibloiteca = new Biblioteca(store);
Now, on querying the library for all representations -
List<String> allLibraryItemsRep = bibloiteca.representationOfAllLibraryItems();
Will return a result having both Movie and Book representations.
On querying the library for specific item types -
String movieRep = bibloiteca.representationOfLibraryItemType(Movie.class);
String bookRep = bibloiteca.representationOfLibraryItemType(Book.class);
Will return specific representations -
Movie [title=Spider-man Homecoming, director=Jon Watts, releaseYear=2017]
Movie [title=Wonder Woman, director=Patty Jenkins, releaseYear=2017]
Movie [title=Guardians of the Galaxy Vol. 2, director=James Gunn, releaseYear=2017]
Book [title=Java 9 Modularity, author=Paul Bakker, publishedYear=2017]
Book [title=Cloud Native Java, author=Josh Long, publishedYear=2017]
Book [title=Effective Java, author=Josh Bloch, publishedYear=2008]
On querying the library for the type which is not present in the library -
String carRep = bibloiteca.representationOfLibraryItemType(Car.class);
Will throw an exception -
java.lang.IllegalArgumentException: Missing type Car
I understand that this is quite a lengthy answer and hope this brought some clarity about the design.
You can create a generic class ListItemsCommand which will accept the item name or class as a parameter for listing and checking for empty list.
And then call ListItemsCommand with the item type like Movie or Book
If you want to remove duplication I suggest using a collect with groupingBy. This allows you to specify which is the key used for deduplication (or grouping) and a reduction function which in case of a duplicate selects the element which is to be selected from the set of duplicates.
Here is a sample method with the groupingBy collector:
public String representationOfAllLibraryItems(Class<? extends LibraryItem> itemType) {
return this.allLibraryItems
.stream()
.filter(libraryItem -> libraryItem.getClass().equals(itemType))
.collect(Collectors.groupingBy(LibraryItem::getName, LinkedHashMap::new,
Collectors.reducing((o1, o2) -> o1.toString().compareTo(o2.toString()) < 0 ? o1 : o2)))
.values()
.stream()
.map(Optional::get)
.map(LibraryItem::representation)
.collect(Collectors.joining(LINE_SEPARATOR));
}
Here is a small test in which we de-duplicate by name of the movie and select the most recent entry in the data:
public static void main(String[] args) {
List<LibraryItem> items = Arrays.asList(new Movie("Valerian", "Luc Besson", "2017"),
new Movie("Valerian", "Luc Besson", "2016"),
new Movie("Spiderman", "Sam Raimi", "2002"),
new Movie("Spiderman", "Sam Raimi", "2001"),
new Movie("Spiderman", "Sam Raimi", "2003"));
Biblioteca biblioteca = new Biblioteca(items);
System.out.println(biblioteca.representationOfAllLibraryItems(Movie.class));
}
The result looks like this:
Luc Besson - Valerian - 2017
Sam Raimi - Spiderman - 2003
Here the de-duplication happens by movie name and the most recent movie is selected.
Related
I'm having issues creating the following two methods using an ArrayList object:
existTextbook(): checks whether a given textbook is in the catalogue. existTextbook() accepts the title and the author and returns true or false. True if the textbook is in the catalogue, false otherwise.
deleteTexbook(): deletes a textbook from the catalogue. deleteTextbook() accepts a textbook title as parameter and deletes the textbook if it exists.
Searching the Java API, the closest method I can find for the first method is the contains method but it takes an object as a parameter, not a String object within the Textbook object like the title or author. The same is true for the remove method for the second method taking an object of the ArrayList as a parameter.
Any hints on how to have a method look at each Textbook object String title or author, then return true if a match is found, or to delete the Textbook object containing the Textbook object String title or author?
Here's my code so far:
Textbook Class
package Ex1;
import java.text.NumberFormat;
public class Textbook
{
private String category, title, author;
private int year;
private double price;
public Textbook (String category, String title, String author, int year,
double price)
{
this.category = category;
this.title = title;
this.author = author;
this.year = year;
this.price = price;
}
public String toString()
{
NumberFormat fmt = NumberFormat.getCurrencyInstance();
String description;
description = "Category: " + category + "\n";
description += "Title: " + title + "\n";
description += "Author: " + author + "\n";
description += "Year: " + year + "\n";
description += "Price: " + fmt.format(price) + "\n" + "\n";
return description;
}
}
Catalogue Class
package Ex1;
import java.util.ArrayList;
public class Catalogue
{
private ArrayList <Textbook> catalogue;
public Catalogue ()
{
catalogue = new ArrayList<Textbook>();
}
public void addTextbook (Textbook t)
{
catalogue.add(t);
}
public boolean existTextbook(String title, String author)
{
}
public void deleteTextbook(String title)
{
}
public String toString()
{
return catalogue.toString();
}
}
Driver Class
package Ex1;
public class Drivermain
{
public static void main(String[] args)
{
Textbook javaBook = new Textbook ("Computer Science",
"Java Software Solutions", "Lewis/Loftus", 2015, 163.45);
Textbook dataBook = new Textbook ("Computer Science",
"Data Structures and Algorithm Analysis in Java,",
"Mark A. Weiss", 2015, 181.90);
Textbook calcBook = new Textbook ("Mathematics",
"Calculus Plus NEW MyMathLab", "Briggs/Cochran/Gillett",
2015, 236.90);
Textbook osBook = new Textbook ("Computer Science",
"Operating Systems: Internals and Design Principles",
"William Stallings", 2015, 205.70);
Textbook historyBook = new Textbook ("History",
"History of the Canadian Peoples: Beginnings to 1867, Vol. 1",
"Conard/Finkel/Fyson", 2015, 96.90);
Catalogue bookCatalogue = new Catalogue();
bookCatalogue.addTextbook(javaBook);
bookCatalogue.addTextbook(dataBook);
bookCatalogue.addTextbook(calcBook);
bookCatalogue.addTextbook(osBook);
bookCatalogue.addTextbook(historyBook);
System.out.println(bookCatalogue);
bookCatalogue.existTextbook("Java Software Solutions", "Lewis/Loftus");
bookCatalogue.deleteTextbook("Java Software Solutions");
}
}
I think instead of using methods from collections, you may want to deal with looking in your Arraylist yourself.
I'm not using a for each loop (just a for loop) because for delete it will cause a concurrent modification exception.
package Ex1;
import java.util.ArrayList;
public class Catalogue
{
private ArrayList <Textbook> catalogue;
public Catalogue ()
{
catalogue = new ArrayList<Textbook>();
}
public void addTextbook (Textbook t)
{
catalogue.add(t);
}
public boolean existTextbook(String title, String author)
{
for(int i =0; i<catalogue.Size(); i++){
Textbook t = catalogue.get(i);
//you'll want getter and setter methods
if(t.author.equals(author)&&t.title.equals(title))
return truel
}
}
public void deleteTextbook(String title)
{
for(int i =0; i<catalogue.Size(); i++){
Textbook t = catalogue.get(i);
if(t.title.equals(title)){
catalogue.remove(i);
}
}
}
public String toString()
{
return catalogue.toString();
}
}
Happy Coding! Leave a comment if you have any questions.
Instead of directly using those methods you may consider just looping through the catalogue ArrayList yourself and testing if the current object matches the title (and author).
It might be overkill, but you could make Textbook implement Comparable or write a Comparator.
I have a four classes Book Disk Paper Magazine all of them derive from another class Items and all of them have a String barcode field.
In another class foo I have ArrayList<Book> books; ArrayList<Disk> disks; ArrayList<Paper> papers; ArrayList<Magazine> magazines;, and I want to implement for each one of these a getByBarcode(String barcode) method, that would look in the arraylist for the item with that barcode. If it's a book it has to look in the books list etc.
Can I avoid having to do four different ones? What I mean is avoiding having to do a getBookByBarcode(String barcode) that would have to look in the books list, getDiskByBarcode(String barcode) that would have to look in the disks list etc..
And have a generic one like public Object getByBarcode(String barcode,type). How do I do this nicely in an OOP way?
Essentially every Book, Magazine, Disk & Paper is an Item. Hence you should have a parent class named Item and not its plural form. Every Base Class should classify as a singular entity.
You should then create objects of Book, Magazine, Disk & Paper. Since all of these classify as an Item, create an array list of type Item and add these objects to the same.
This way you have a item list which has books, magazines, disks & papers. You can then look into this list for an item with barcode and get the type of item.
Source Code:
Item.java
package myLibrary;
public class Item {
protected String barcode;
public String getBarcode() {
return barcode;
}
public void setBarcode(String barcode) {
this.barcode = barcode;
}
public Item(String barcode) {
this.barcode = barcode;
}
}
Book.java / Disk.java / Magazine.java / Paper.java
package myLibrary;
public class Book extends Item {
public Book(String barcode) {
super(barcode);
}
public String getBarcode() {
return barcode;
}
}
Lecturer.java (Class containing main function)
package myCollege;
import java.util.ArrayList;
import myLibrary.Book;
import myLibrary.Disk;
import myLibrary.Item;
import myLibrary.Magazine;
import myLibrary.Paper;
public class Lecturer {
public static void main(String[] args) {
// Declare items
Book x1 = new Book("Item101");
Book x2 = new Book("Item102");
Disk x3 = new Disk("Item201");
Disk x4 = new Disk("Item202");
Magazine x5 = new Magazine("Item301");
Paper x6 = new Paper("Item401");
Paper x7 = new Paper("Item402");
ArrayList<Item> items = new ArrayList<>();
items.add(x1);
items.add(x2);
items.add(x3);
items.add(x4);
items.add(x5);
items.add(x6);
items.add(x7);
String itemType = getItemByBarcode("Item202", items);
System.out.println(itemType);
}
private static String getItemByBarcode(String barcode, ArrayList<Item> items) {
String itemType = "";
for(Item i : items) {
if(i.getBarcode().equalsIgnoreCase(barcode)) {
itemType = i.getClass().getSimpleName();
}
}
return itemType;
}
}
Output:
Disk
I hope this helps in better understanding of the concept/issue.
may sound hackish but you can do like some other users suggested and call
public Object getByBarcode(String barcode, Class<T> classy)
{
ArrayList<Items> items = null;
if(classy.class.getSimpleName().equals(Book.class.getSimpleName()))
items = bookArray;
else if(classy.class.getSimpleName().equals(Magazine.class.getSimpleName()))
items = magazineArray;
else
... cnt'd
for(Item i : items)
if( i.getBarCode().equals(barcode) return i;
}
Then to call this beastly mess you could do something like . . .
Item i = getByBarCode("0932A3", Book.class);
Hello I am new to Java and NetBeans and am in Advanced classes in which my classes are 5 weeks long, so it is a lot to learn a new code language in 5 weeks. Anyways I have an assignment to Create a class named Movie that holds a movie name and rating. Provide methods to get and set both the movie name and rating. Create a class named TestMovie that creates three Movie instances with different values for name and rating and prints the information about each movie. I have done the code and it is passing the build fine but my professor wants a screen shot of the program working and running but I can't get NetBeans to bring that up. The chapter on building the test project was ripped out of my book. Can I get some help or pointers here is the code I have done:
package movie;
/**
*
* #author Jason
*/
public class Movie {
String movieRating;
public Movie(String rated, String mtitle) {
this.mrating = rated;
this.title = mtitle;
}
public void setRating(String Rating) {
movieRating = Rating;
}
// Get the rating
public String getRating() {
return movieRating;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
#Override
public String toString() {
return "Movie" + " title=" + getTitle() + " rating=" + getRating();
}
public static void main(String args[]) {
Movie mv = new Movie("", "");
mv.toString();
}
private String title;
private String mrating;
}
You can just run a test on the console, that is, create a MovieTest class with only a main method and create three instances/objects of Movie (Movie m1, m2, m3; OR Movie[] movies;). Assign them values either in the constructor or with the set methods then print them out with the method print or println in System.out.
Something along the lines of:
public class MovieTest {
public static void main(String[] args) {
Movie[] movies = new Movie[] {new Movie("R1", "T1"), new Movie("R2", "T2"), new Movie("R3", "T3)";
for (Movie i : movies) {
System.out.println(i.toString());
}
}
}
Then end by screenshooting the results.
As an alternative to the other answers suggesting printing the output to the console, with the Netbeans' UI editor you can easily create a window with a label showing the result, which makes it slightly more fancy.
You can get details on how to this here. Here's an image from that page:
The full working code is here. As you can see, it's just a few extra lines.
Your application prints no output, because you invoke toString(), but you don't print the result of it.
An example to create 3 Movie instances with data,
print them out, and then make a screenshot of your console app.
public static void main(String args[]) {
List<Movie> movieList = new ArrayList<Movie>(3);
Movie mv1 = new Movie("very Good", "Testfilm 1");
movieList add(mv1);
mv1 = new Movie("good", "Testfilm 2");
movieList add(mv1);
mv1 = new Movie("not good", "Testfilm 2");
movieList add(mv1);
for (Movie m : movieList) {
System.out.println(m.toString());
}
}
Immutable classes are great but there is one big problem i cant think of a sensible way to solve - cycles.
class Friend {
Set<Friend> friends();
}
How does one model Me having You as a friend who in turn has me as a Friend back ?
IMMUTABILITY
This class from the outside world should definitely be immutable. The value held internally should be constant for the purposes of equality checks.
[[[ Edit: Added code to demonstrate fully immutable concept ]]]
That's why builders are so nice for immutables - they allow mutability during construction to get everything set before you "freeze" it. In this case, I guess you need a Friend builder that supports creating cycles.
final FriendBuilder john = new FriendBuilder().setName("john");
final FriendBuilder mary = new FriendBuilder().setName("mary");
final FriendBuilder susan = new FriendBuilder().setName("susan");
john
.likes(mary)
.likes(susan);
mary
.likes(susan)
.likes(john);
susan
.likes(john);
// okay lets build the immutable Friends
Map<Friend> friends = FriendsBuilder.createCircleOfFriends(john, mary, susan);
Friend immutableJohn = friends.get("john");
Edit: Added immutable example below to demonstrate approach:
There was some discussion in the comments about whether an immutable version was possible.
Fields are final and immutable. A modifiable set is used in the constructor, but it only the unmodifiable reference is kept after construction.
I have another version that uses Guava ImmutableSet for a truly immutable set rather than JDK's unmodifiable wrapper. It works the same, but uses Guava's nice set builder.
Code:
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
/**
* Note: potentially cycle graph - be careful of deep equals/hashCode/toString/etc.
* Immutable
*/
public class Friend {
public static class Builder {
private final String name;
private final Set<Builder> friends =
new HashSet<Builder>();
Builder(final String name) {
this.name = name;
}
public String getName() {
return name;
}
public Set<Builder> getFriends() {
return friends;
}
void likes(final Builder... newFriends) {
for (final Builder newFriend : newFriends)
friends.add(newFriend);
}
public Map<String, Friend> createCircleOfFriends() {
final IdentityHashMap<Builder, Friend> existing =
new IdentityHashMap<Builder, Friend>();
// Creating one friend creates the graph
new Friend(this, existing);
// after the call existingNodes contains all the nodes in the graph
// Create map of the all nodes
final Map<String, Friend> map =
new HashMap<String, Friend>(existing.size(), 1f);
for (final Friend current : existing.values()) {
map.put(current.getName(), current);
}
return map;
}
}
final String name;
final Set<Friend> friends;
private Friend(
final Builder builder,
final Map<Builder, Friend> existingNodes) {
this.name = builder.getName();
existingNodes.put(builder, this);
final IdentityHashMap<Friend, Friend> friends =
new IdentityHashMap<Friend, Friend>();
for (final Builder current : builder.getFriends()) {
Friend immutableCurrent = existingNodes.get(current);
if (immutableCurrent == null) {
immutableCurrent =
new Friend(current, existingNodes);
}
friends.put(immutableCurrent, immutableCurrent);
}
this.friends = Collections.unmodifiableSet(friends.keySet());
}
public String getName() {
return name;
}
public Set<Friend> getFriends() {
return friends;
}
/** Create string - prints links, but does not traverse them */
#Override
public String toString() {
final StringBuffer sb = new StringBuffer();
sb.append("Friend ").append(System.identityHashCode(this)).append(" {\n");
sb.append(" name = ").append(getName()).append("\n");
sb.append(" links = {").append("\n");
for (final Friend friend : getFriends()) {
sb
.append(" ")
.append(friend.getName())
.append(" (")
.append(System.identityHashCode(friend))
.append(")\n");
}
sb.append(" }\n");
sb.append("}");
return sb.toString();
}
public static void main(final String[] args) {
final Friend.Builder john = new Friend.Builder("john");
final Friend.Builder mary = new Friend.Builder("mary");
final Friend.Builder susan = new Friend.Builder("susan");
john
.likes(mary, susan);
mary
.likes(susan, john);
susan
.likes(john);
// okay lets build the immutable Friends
final Map<String, Friend> friends = john.createCircleOfFriends();
for(final Friend friend : friends.values()) {
System.out.println(friend);
}
final Friend immutableJohn = friends.get("john");
}
}
Output:
Node 11423854 {
value = john
links = {
susan (19537476)
mary (2704014)
}
}
Node 2704014 {
value = mary
links = {
susan (19537476)
john (11423854)
}
}
Node 19537476 {
value = susan
links = {
john (11423854)
}
}
The correct way to model a cycle is with a Graph. And a single source code line comment can be enough to enforce inmutability: "can't touch this".
What kind of inmutable enforcement are you looking for? Do you want a a velociraptor to appear whenever you modify the inmutable Set? The difference between mutable and inmutable is just a convention. However, the bits on the RAM can be easily modified and with the Reflection API you can break any encapsulation and data hiding conventions.
Ignoring the velociraptor for a moment, Java does not support an inmutable type. As a workaround, you need to model a datatype that behaves like one.
And for the inmutable property to make sense you need to make Friend an interface, having one implementing class: InmutableFriend, and the construction of the object should fully happen inside the constructor.
Then, since the graph contains cycles, before creating the final inmutable instances you need to store the graph nodes in some mutable temporary structure. You also need to return an unmodifiableSet on the InmutableFriend.friends() method.
Finally, to clone the graph you need to implement a Deep-copy algorithm like Breadth-first search on the Mutable graph. One question though is what happens when the graph is not fully connected.
interface Friend {
public Set<Friend> friends();
}
class MutableFriend {
private Set<MutableFriend> relations = new HashSet<MutableFriend>();
void connect(MutableFriend otherFiend) {
if (!relations.contains(otherFriend)) {
relations.add(otherFiend);
otherFriend.connect(this);
}
}
Friend freeze() {
Map<MutableFriend, InmutableFriend> table = ...;
/*
* FIXME: Implement a Breadth-first search to clone the graph,
* using this node as the starting point.
*
* TODO: If the graph is not connected this won't work.
*
*/
}
}
class InmutableFriend() implements Friend {
private Set<Friend> connections;
public Set<Friend> friends() {
return connections;
}
public InmutableFriend(Set<Friend> connections) {
// Can't touch this.
this.connections = Collections.unmodifiableSet(connections);
}
}
Immutability doesn't need to be compiler-enforced to be valid architecturaly. You can have a legitimate immutable object that takes post-construction initialization parameters. For instance...
private Object something;
public void init( final Object something )
{
if( this.something != null )
{
throw new IllegalStateException();
}
this.something = something
}
The member field "something" isn't final, but it cannot be set more than once either.
A more complex variant based on discussion in comments...
private boolean initialized;
private Object a;
private Object b;
public void init( final Object a, final Object b )
{
if( this.initialized )
{
throw new IllegalStateException();
}
this.initialized = true;
this.a = a;
this.b = b;
}
public Object getA()
{
assertInitialized();
return this.a;
}
public Object getB()
{
assertInitialized();
return this.b;
}
private void assertInitialized()
{
if( this.initialized )
{
throw new IllegalStateException( "not initialized" );
}
}
I am trying to make a project that adds cd / dvd /movie info from main() to a collections library
then prints info added.
like: output
-Book-
author: Robert A. Heinlein
# pages: 325
title: Starship Troopers
keywords: science fiction, war, weapons
-Music-
band: five finger death punch
# songs: 15
members: Zoltan Bathory, Ivan Moody,Jeremy Spencer,Matt Snell,Jason Hook
title: War is the answer
keywords: rock
I currently have 6 classes
1.project1 - main()
2.Library - where im adding to database
3.item - inheritance(title & number)
4.cd
5.dvd
6.movie
i am trying to use inheritance so i want to keep the files i have.
My question is i am trying to add to the collections in the library class. I am just not sure how to do it.
here is the classes i think you will need to see..
import java.io.PrintStream;
import java.util.Collection;
public class project
{
private static Library library = new Library();
public static void main(String[] args)
{
PrintStream out = System.out; // we will be printing to the standard output stream
Item item;
// add items to library
out.println(">>> adding items to library:\n");
item = library.addBook("The Curious Incident of the Dog in the Night-Time", "Mark Haddon", 240, "autism", "Asperger's Syndrome");
if (item != null)
library.printItem(out, item);
item = library.addBook("Starship Troopers", "Robert A. Heinlein", 325, "science fiction", "war", "weapons");
if (item != null)
library.printItem(out, item);
item = library.addBook("The Moon Is A Harsh Mistress", "Robert A. Heinlein", 389, "science fiction", "moon", "social structures");
if (item != null)
library.printItem(out, item);
item = library.addMusicCD("Europe In '72", "Grateful Dead", 12, "acid rock", "sixties", "jam bands");
if (item != null) {
library.addBandMembers(item, "Jerry Garcia", "Bill Kreutzman", "Keith Godcheaux");
library.printItem(out, item);
}
item = library.addMusicCD("Don't Let Go", "Jerry Garcia Band", 15, "acid rock", "jam bands");
if (item != null) {
library.addBandMembers(item, "Jerry Garcia", "Keith Godcheaux");
library.printItem(out, item);
}
item = library.addMusicCD("Sergeant Pepper's Lonely Hearts Club Band", "Beatles", 10, "acid rock", "sixties");
if (item != null) {
library.addBandMembers(item, "John Lennon", "George Harrison", "Ringo Starr");
library.printItem(out, item);
}
item = library.addMovieDVD("Lost In Translation", "Sofia Coppola", 14, "Japan", "loneliness");
if (item != null) {
library.addCast(item, "Bill Murray", "Scarlett Johansson");
library.printItem(out, item);
}
item = library.addMovieDVD("Groundhog Day", "Harold Ramis", 14, "newscaster", "groundhog", "time");
if (item != null) {
library.addCast(item, "Bill Murray", "Andie MacDowell");
library.printItem(out, item);
}
// print books, musicCDs, movies
out.println(">>> books:\n");
printItems(out, library.books());
out.println(">>> music CDs:\n");
printItems(out, library.musicCDs());
out.println(">>> movies:\n");
printItems(out, library.movies());
// print items for keyword
printItemsForKeyword(out, "science fiction");
printItemsForKeyword(out, "jam bands");
printItemsForKeyword(out, "xxx");
// items by artist
out.println(">>> books by Robert A. Heinlein:\n");
printItems(out, library.booksByAuthor("Robert A. Heinlein"));
out.println(">>> music by the Grateful Dead:\n");
printItems(out, library.musicByBand("Grateful Dead"));
out.println(">>> music by the Rolling Stones:\n");
printItems(out, library.musicByBand("Rolling Stones"));
out.println(">>> movies by Sofia Coppola:\n");
printItems(out, library.moviesByDirector("Sofia Coppola"));
out.println(">>> music by Jerry Garcia:\n");
printItems(out, library.musicByMusician("Jerry Garcia"));
out.println(">>> movies with Bill Murray:\n");
printItems(out, library.moviesByActor("Bill Murray"));
}
private static void printItemsForKeyword (PrintStream out, String keyword)
{
Collection<Item> items;
out.printf(">>> items for keyword: %s\n\n", keyword);
items = library.itemsForKeyword(keyword);
printItems(out, items);
}
private static void printItems (PrintStream out, Collection<Item> items)
{
if (items != null && items.size() > 0)
for (Item item : items)
library.printItem(out, item);
else
out.println("none\n");
}
}
here is the library class where i am having trouble adding to the collections..
How would i add a book or a cd to the collections?
import java.io.PrintStream;
import java.util.Collection;
import java.util.*;
public class Library
{
// returns all of the items which have the specified keyword
public Collection<Item> itemsForKeyword(String keyword)
{
return null;
}
// print an item from this library to the output stream provided
public void printItem(PrintStream out, Item item)
{
}
// adds a book to the library
public Item addBook(String title, String author, int nPages, String... keywords)
{
return null;
}
// returns all of the books by the specified author
public Collection<Item> booksByAuthor(String author)
{
return null;
}
// returns all of the books in the library
public Collection<Item> books()
{
return null;
}
// music-related methods
// adds a music CD to the library
public Item addMusicCD(String title, String band, int nSongs, String... keywords)
{
Collection MusicCollection = new HashSet();
MusicCollection.add(title);
return null;
}
// adds the specified band members to a music CD
public void addBandMembers(Item musicCD, String... members)
{
}
// returns all of the music CDs by the specified band
public Collection<Item> musicByBand(String band)
{
return null;
}
// returns all of the music CDs by the specified musician
public Collection<Item> musicByMusician(String musician)
{
return null;
}
// returns all of the music CDs in the library
public Collection<Item> musicCDs()
{
return null;
}
// movie-related methods
// adds a movie to the library
public Item addMovieDVD(String title, String director, int nScenes, String... keywords)
{
return null;
}
// adds the specified actors to a movie
public void addCast(Item movie, String... members)
{
}
// returns all of the movies by the specified director
public Collection<Item> moviesByDirector(String director)
{
return null;
}
// returns all of the movies by the specified actor
public Collection<Item> moviesByActor(String actor)
{
return null;
}
// returns all of the movies in the library
public Collection<Item> movies()
{
return null;
}
}
here is the items class
import java.io.PrintStream;
import java.util.Collection;
import java.util.*;
public class Item
{
private String title;
private int number;
public Item(String theTitle, int theNumber)
{
number = theNumber;
title = theTitle;
}
public String getTitle()
{
return title;
}
public int getNumber()
{
return number;
}
}
here is the cd class - the dvd class is almost identical
import java.util.*;
public class CD extends Item
{
private String artist;
private String members;
public CD(String theTitle, String theArtist, String theMembers, int number)
{
super(theTitle,number);
artist = theArtist;
members = theMembers;
}
public String getArtist()
{
return artist;
}
public String getMembers()
{
return members;
}
public void print()
{
System.out.println("-Music-");
System.out.println("band: " + artist);
}
}
I am not sure if i could combine the cd/dvd/movie classes into items class?
My main QUESTION is:
how should i add each cd/dvd to collections?????
in the library class
would i just define a collection to add in every addfunction(addMusicCD,addBandMembers,addMovieDVD,etc..) or should i put the collection in the beginning of the class? and how do i add to that collection???
public Item addMusicCD(String title, String band, int nSongs, String... keywords)
{
Collection MusicCollection = new HashSet(); // how should i add each cd/dvd to collections?????
MusicCollection.add(title);
return null;
}
I am also trying to return an Item and cannot! what item would i need to return??
I know this is alot of information. Sorry.
Hopefully someone can help me and not just make fun of me. i am trying to learn java from c++
Thank you for any help you can give me..
I think you need to study Object Orientation Principles a bit more.
The Library should be able to add Items. The Library methods addBook(many params) and addDVD() etc should be replaced by a more generic addItem(Item item, String... keywords).
Items can be CDs, DVDs or Movies. It's up the the CD class to add band members, not the Library class.
Adding an item to the library becomes something like
CD cd = new CD("Europe In '72", "Grateful Dead", 12);
cd.addBandMembers("Jerry Garcia", "Bill Kreutzman", "Keith Godcheaux");
library.addItem(cd, "acid rock", "sixties", "jam bands"));
Hope this helps a little to get you on track.
In addMusicCD, do a new CD(the various bits).
Add the resulting CD to the collection.
Return it.
It might be easier for you to fit this all together if you used Generics to declare collections, e.g.
class Library {
private Set<CD> theCDs;
public Item addCD(String title) {
CD cd = new CD(title);
theCDS.add(cd);
return cd;
}
}
etc, etc. etc.
I would probably add another tree of inheritance, so a Library is an abstract subclass of e.g. HashSet:
public abstract class Library<T extends Item> extends HashSet<T>{
...
abstract void addItem(T item);
}
Then you create all your libraries by subclassing your Library-class:
public class CDLibrary extends Library<Cd>{
...
#Override
public void addItem(Cd item){
// Maybe add some information of cd to a hashmap for lookups, or whatever
...
this.add(item);
}
When you're subclassing HashSet, all the add and delete operations are done for you.
If you dont need specific add-methods for each Item, you can simply remove the abstract mathod of Library, and just take advantage of the generic-syntax, to specify a new type of library when subclassing.
You might consider subclassing ArrayList, or something similar instead, as HashSet does not have a get() method, which ArrayList does, the above code was just an example of what you could do.
If its unclear, I'll try to clarify a bit more! But I hope you get the picture - subclassing to inherit the functions you need, instead of creating them again. Generics, (do you know generics?) are to ensure type-safety, so you cannot add a DVD to a CDLibrary.
Also, be sure to override equals() and hashcode() of your Items, to make sure you can distinguish between two Items.
I hope it makes sense!
Do you need to have the CDs, DVDs, and Books into separate data structures? What's your use case? If you need to get them in a separate way, then having different data structures is OK.
Otherwise, and I think this is the case, I think you could be fine having a Set<Item> and dump all your items in it.
public static class Library{
private Set<Item> items = new HashSet<Item>();
public void add(Item i){
items.add(i);
}
public String toString(){
return items.toString()
}
}
And, in your Item sub-classes, you have to have toString() overridden, and everything will print itself just fine.