I am trying to implement a room booking system with an Observable design pattern using Firebase. I am pretty new to Observable pattern, but I have created the general files as such:
Subject.java
public interface Subject {
void Attach(Observer o);
void Detach(Observer o);
void refreshAccess();
}
Observer.java
public interface Observer {
void update(String newAuthorKey);
}
Booking.java
public class Booking implements Subject {
private String authorKey; //authorKey for the booking
private ArrayList<Observer> observers; //users registered for the room
public Booking(){
observers = new ArrayList<Observer>();
}
public void Attach(Observer o){
observers.add(o);
}
public void Detach(Observer o){
observers.remove(o);
}
public void refreshAccess(){
}
public void refreshAccess(String newAuthorKey){
this.authorKey = newAuthorKey;
this.NotifyObservers();
}
private void NotifyObservers(){
for (Observer o:observers)
o.update(this.authorKey);
}
}
User.java
public class User implements Observer {
private String authorKey;
private Subject subject;
public User(Subject subject){
this.subject = subject;
//register itself to the subject
this.subject.Attach(this);
}
#Override
public void update(String newAuthorKey){
//get update from subject
this.authorKey = newAuthorKey;
//do something according to the update
}
}
Essentially, the idea is that when a room is booked, a new Booking object gets initialised, and the user who books it (and any of the other user he chooses to share this booking with) gets added as users into this booking. How can I update this information into Firebase (I have set it up already in my application), and how can I get a list of all the Bookings that each user subscribed to? I have a fragment on my android app that handles all the event listeners, etc, but I have no idea how to link it up to these classes with Firebase at the moment.
Much appreciated! :)
The pattern Observable works with one or more Observer attached to one ore more Observable. Try this:
public class Booking extends Observable {
private String authorKey; //authorKey for the booking
// private ArrayList<Observer> observers; //users registered for the room (no need because Observable holds a list of Observer already)
public Booking(String authorKey){
this.authorKey = authorKey;
}
public void refreshAccess(){
nofityObservers(null);
}
public void refreshAccess(String newAuthorKey){
String oldAuthorKey = this.authorKey;
this.authorKey = newAuthorKey;
nofityObservers(oldAuthorKey);
}
#Override
public void notifyObservers(Object arg) {
setChanged();
super.notifyObservers(arg);
}
}
public class User implements java.util.Observer {
private ArrayList<String> authorKeys; // to get all bookings a user has
public User(){
}
public void addBooking(Booking b) {
b.addObserver(this);
}
#Override
public void update(Observable o, Object arg) {
if (!(o instanceof Booking ))
return;
Booking b = (Booking) o;
if(arg != null)
authorKeys.remove(arg);
authorKeys.add(b.getAuthorKey());
}
}
And somewhere in your system:
User u1 = new User();
User u2 = new User();
Booking b = new Booking(authorKey1);
b.addObserver(u1);
b.addObserver(u2);
b.refreshAccess(); // u1 and u2 get notified
b.deleteObserver(u2); // you might want to override this to notify user remove booking info
b.refreshAccess(authorKey2); // u1 get notified
Related
I'm designing a music information system. I have a couple of entities that are connected to each other.
Below is part of the domain code.
class Album {
private Set<Track> tracks;
private boolean published;
public Set<Track> getTracks() {
return this.tracks;
}
public boolean isPublished() {
return this.published;
}
public void publish() {
System.out.println("Album.publish() called");
this.published = true;
this.tracks.forEach(track -> track.publish());
}
}
class Track {
private boolean published;
private Album album;
public boolean isPublished() {
return this.published;
}
public Album getAlbum() {
return this.album;
}
public void publish() {
// if track is single (this.album == null), set published to true
// if track is part of an album and the album is NOT published, return;
// if track is part of an album and the album is published, set published to true
if(this.album != null && !this.album.isPublished())
return;
this.published = true;
}
}
Track is an independent entity. It can be a single track (I.e. without an Album). So the album attribute is actually needed.
One domain rule is that when an album is archived (i.e. not published), its tracks cannot be published neither and if an album is published, any of its tracks can either be published or archived.
The problem is that when an album is published (e.g. album1.publish()), its tracks' publish() method is called as well. But track1.publish() checks if the album is published based on the copy it already has (which is not published).
How can I solve the problem?
If you split domain model entities by behaviour, you can get rid of described limitations
Let's have some interfaces for such entities:
interface AlbumId{
String asString();
AlbumId Absent = () -> "NO ALBUM AT ALL";
}
interface Publication{
void publish() throws Exception;
void archive() throws Exception;
boolean published();
}
interface Track{
TrackId id();
AlbumId albumId(); //smart type (as DDD suggest), therefore, no more nulls
}
Now you may enforce rules by creating class that will get you a list of tracks you can publish:
public class TracksReadyToPublishOf implements Supplier<Map<TrackId, TrackPublication>>{
//this class may access to cache and have dosens of other optimizations
public TracksReadyToPublishOf(AlbumId id){...}
#Override public get(){...}
}
Then you can reuse your code to check your rules anywhere:
public class TrackPublication implements Publication {
private final Track track;
private final Supplier<Map<TrackId, TrackPublication>> allowedTracks;
//easy for unit testing
public SmartTrackPublication(Track track, Supplier<Map<TrackId, TrackPublication>> allowedTracks){
this.track = track;
this.allowedTracks = allowedTracks;
}
public SmartTrackPublication(Track track){
this(track, new TracksReadyToPublishOf(track.albumId());
}
#Override
public publish() throws AlbumArchivedException{
if(this.albumId != AlbumId.Absent){
if(!this.allowedTracks.get().containsKey(this.track.id())){
throw new AlbumArchivedException();
}
}
this.allowedTracks.get().get(this.id()).publish();
}
}
And for album publishing:
public class AlbumPublication implements Publication{
private final AlbumId id;
private final Producer<Map<TrackId, TrackPublication>> tracks
private AlbumWithTracks(AlbumId id, Producer<Map<TrackId, TrackPublication>> tracks){
this.id = id;
this.tracks = tracks;
}
public AlbumWithTracks(AlbumId id){
this(id, new TracksReadyToPublishOf(id))
}
...
#Override publish() throws Exception{
//code for publishing album
for(TrackPublication t : Arrays.asList(
this.tracks.get()
)){
t.publish(); //track can publish anyway if it presents in list above
}
}
}
I'm having a problem with inheritance. I've illustrated it with those account classes :
class Account {
accountLevel = BASIC;
connect() {...}
changePassword() { ... }
ChangeEmail() { ... }
}
class CustomerAccount extends Account {
accountLevel = CUSTOMER;
createOrder() { ... }
payOrder() { ... }
}
class AdminAccount extends Account {
accountLevel = ADMIN;
addProduct() { ... }
deleteProduct() { ... }
}
If I have an instance of an Account and I want to cast it to a AdminAccount to do addProduct() is it okay to do that :
Account account = new AdminAccount();
if(account.accountLevel == ADMIN){
AdminAccount adminAccount = (AdminAccount) account;
adminAccount.addProduct();
}
Edit:
I want to have all my accounts in the same List, because they all use the same connect method. But when the account is connected I still have an account object but I want to use their dynamic type methods, so i'm forced to cast the object.
It's almost like an instanceof but with extra steps. I feel like having to cast the account isn't very OOP, is there another more elegant solution ? Maybe not use inheritance between the accounts classes ?
One way to approach this is:
AdminAccount account = new AdminAccount();
account.addProduct();
If the expectation is only an Admin Account can add a product, relevant methods should expect an Admin Account in the input instead of an account. Is there a reason the instance is being created as an Account instead of an Admin Account?
Edit - Including sample packaging type handling into a manager class.
public static void main(String args[])
{
AccountManager manager = new AccountManager();
manager.addAccount(new User());
manager.addAccount(new User());
manager.addAccount(new Admin());
System.out.println(manager.getUsers().collect(Collectors.toList()));
}
public static final class AccountManager
{
private final List<Account> allAccounts = new ArrayList<>();
public Stream<User> getUsers(){ return getAccountType(User.class); }
public Stream<Admin> getAdmins(){ return getAccountType(Admin.class); }
public Stream<Account> getAccounts(){ return getAccountType(Account.class); }
public <T extends Account> void addAccount(T account) { if(account.connect()) allAccounts.add(account); }
#SuppressWarnings("unchecked") private <T> Stream<T> getAccountType(Class<T> type){ return allAccounts.stream().filter(type::isInstance).map(act -> (T) act); }
}
public static final class User extends Account{}
public static final class Admin extends Account{}
public static class Account { boolean connect(){ return true; } }
In general, it's considered not to be a good practice to have if statements by some type attribute or by instanceof checks.
An alternative to this is the visitor pattern. Here's a sketch of what you could do:
interface IAccount {
void process(AccountProcessor processor);
}
interface AccountProcessor {
void processBasicAccount(Account acc);
void processCustomerAccount(CustomerAccount acc);
void processAdminAccount(AdminAccount acc);
}
The idea is that you have an interface that defines a process method for all the accounts, that receives an AccountProcessor (you can change the names here, this is just to show an example). There should be one method for every different type of account. You could even use method overloading, i.e. all methods could be named something like processAccount and receive a specialized account type.
Now, your Account class and all its subtypes should implement the IAccount interface:
class Account implements IAccount {
#Override
void process(AccountProcessor processor) {
processor.processBasicAccount(this);
}
// all the other Account stuff
}
class CustomerAccount implements IAccount {
#Override
void process(AccountProcessor processor) {
processor.processCustomerAccount(this);
}
// all the other CustomerAccount stuff
}
class AdminAccount implements IAccount {
#Override
void process(AccountProcessor processor) {
processor.processAdminAccount(this);
}
// all the other AdminAccount stuff
}
Now, with all these pieces in place, you are ready to process your list of accounts:
class AccountProcessorImpl implements AccountProcessor {
private List<Account> accounts; // filled with all the accounts
void doSomethingWithAllTheAccounts() {
// iterate all the accounts and process each one of them
accounts.forEach(IAccount::process);
}
#Override
public void processBasicAccount(Account acc) {
// do something with the basic account
}
#Override
public void processCustomerAccount(CustomerAccount acc) {
// do something with the customer account
}
#Override
public void processAdminAccount(AdminAccount acc) {
// do something with the admin account
}
}
I hope you grasp the general idea. This is just a sketch to show you the core technique. There might be variants, i.e. you might have separate processors for each different type of account, or you might process the list of accounts in a class other than the AccountProcessorImpl, etc.
It's really OK. We call it polymorphism in OOP. Parents can be cast to all children.
I have three classes:
public AccountTable { void insert(Account account){}}
public CustmerTable { void insert(Customer customer){}}
public TransactionTable { void insert(Transaction Transaction){}}
and an endpoint which receives a message class for insertion as:
public message {
Entity entity; //after deserialization, becomes Account, Customer or Transaction
String entityName; //contains class of the entity
}
How can I redirect to the correct insertion method? The only solution I'm seeing is check using istanceOf and use switch/if, but I don't think that's much scalable if more classes get added, and I can't change Customer, Account or Transaction, as they are legacy code and very sensitive to changes.
I believe you could do something like this:
public interface Saveable <T> {
void save(T entity);
}
public class AccountSaver implements Saveable<Account> {
void save(Account account){
// ... save account
}
}
public class CustomerSaver implements Saveable<Customer> {
void save(Customer customer){
// ... save customer
}
}
public class TransactionSaver implements Saveable<Transaction> {
void save(Transaction transaction){
// ... save transaction
}
}
Well, if Class1/2/3 are service you manage, and you already managed json to right class,
then i'll create a service like
public class MyService {
#Autowired
List<ClasseX> services;
public void save(Object obj){
for (ClasseX service : services) {
if(service.accept(obj)){
service.save(obj);
break;
}
}
}
and add a method on ClassX, where each instance will do the instanceOf check
public boolean accept(Object obj)
I'm working on a small project where I want to have a list of a class called "DevelopmentEmployee", but only one of them is allowed to manipulate certain methods in another class "Project". The way I have implemented it, the class Project has a field called projectLeader, which is of the type DevelopmentEmployee. When a DevelopmentEmployee attempts to access methods in the class Project, I want to check if the DevelopmentEmployee is equal to the specific instance of Project's projectLeader.
Something like
public class Project {
private DevelopmentEmployee projectLeader;
private List < Activity > activities = new ArrayList < Activity > ();
public Project(DevelopmentEmployee pL) {
this.projectLeader = pL;
}
public void addActivity(String activityName) {
if (projectLeader.equals(DevelopmentEmployee * ) {
activities.add(activity);
}
}
}
But I can't figure out a way to make the access requirement work. How can the instance of the class Project know who is trying to access it?
You should also pass the DevelopementEmployee in addActivity for checking it against the projectLeader.
public void addActivity(String activityName,DevelopmentEmployee employee) {
if (projectLeader.equals(employee) {
activities.add(activity);
}
}
Then you need to override equals method in DevelopmentEmployee class, for proper checking of equality, like the one as shown below :
public boolean equals(DevelopementEmployee e){
if(e!=null && this.employeeId==e.employeeId)
return true;
else
return false;
}
Several possibilities come to mind:
Provide the instance of the one accassing the project method to the method:
public void addActivity(String activityName, DevelpmentEmployee user) {
if (projectLeader.equals(user)) {`
Create some class that holds information about active user and use that inside the methods:
public class Project {
private UserRegistry userRegistry;
private List<Activity> activities = new ArrayList<Activity>();
public Project(UserRegistry userRegistry) {
this.userRegistry = userRegistry;
}
public void addActivity(String activityName) {
if (userRegistry.isActiveUserProjectLeader()) {
activities.add(activity);
}
}
}
public class UserRegistry {
private DevelpmentEmployee projectLeader;
private DevelpmentEmployee activeUser;
private List<DevelpmentEmployee> user;
public void addUser(DevelpmentEmployee user) { ... }
public void makeProjectLeader(DevelpmentEmployee newLeader) { ... }
public void makeActiveUser(DevelpmentEmployee newActiveUser) { ... }
public boolean isActiveUserProjectLeader() { ... }
}`
Please note: I am new in this subject.
Suppose I have a class Event.
public class Event {
//constructors, etc.
public void pathFollowed(int location) {
//this method could be called at any time
}
}
And a class called EventManager.
public class EventManager {
private int managerLocation;
private ArrayList<Event> events;
public EventManager() {
events = new ArrayList<Event>();
}
public void addEvent(Event e) {
//THIS IS THE AREA OF INTEREST
events.add(e);
}
}
In the "area of interest" comment, is there any way of setting the value of managerLocation whenever the Event e calls upon pathFollowed(int location). My goal is that when any of the Events in the events arraylist calls pathFollowed(int location) that managerLocation would be set to "location" ("location" referring to the input in the pathfollowed method).
I was originally thinking of over-riding the pathFollowed method, but then I realized this can't be done because by the time the event gets to the addEvent method, it is already instantiated and can't be changed in this manner.
Thanks in advance.
Maybe some kind of listener pattern?
public class Event {
private List<PathListener> pls; //Or just one if you know you'll only need one
//constructors, etc.
public void pathFollowed(int location) {
//this method could be called at any time
for(PathListener pl : pls)
pl.notifyLocation(location);
}
public addPathListener(PathListener pl) {
pls.add(pl);
}
}
EventManager:
public class EventManager implements PathListener {
private int managerLocation;
private ArrayList<Event> events;
public EventManager() {
events = new ArrayList<Event>();
}
public void addEvent(Event e) {
e.addPathListener(this);
events.add(e);
}
#Override
public notifyLocation(int location) { //Of the PathListener interface
managerLocation = location;
}
}
This is just a kind-of-generic example, because I don't know what your purpose is, but maybe it will get you thinking.