Questo è un esempio di dipendenza ciclica?

2

Sono uno studente universitario e ho questo incarico su un simulatore di eventi discreti. Attualmente ho completato alla fine della v1.2 qui . In allegato anche una panoramica algoritmica.

Hoscrittoilmiocodiceinquantotaleperrisolvereilproblemainquestione.Questocodicesuperatuttiicasiditestforniti,mapensochecisiasicuramentespaziopermiglioramentinell'aspettoprogettuale.HoinventatoquestocodiceestraendoilproblemaerisolvendoloinmodoproceduraleperpoiriprogettarloinOO.PersonalmentesonovenutodaunbackgrounddiPythoneorastoimparandoJavaperquestomodulo,quindistoancoraprendendolecordesuldesignOOPemistoliberandodituttoil"mindset procedurale" il più possibile.

Nota: ho accesso privato a RandomGenerator poiché mi è stato fornito un file compilato con cui lavorare nel mio pacchetto.

principale

import cs2030.simulator.DiscreteEventSimulator;

/**
 * Contains main method to start the programme.
 */
class Main {

    /**
     * Starts the Discrete Event Simulator.
     */
    public static void main(String[] args) {
        DiscreteEventSimulator.start();
    }
}

DiscreteEventSimulator

package cs2030.simulator;

import java.util.Scanner;

/** 
 * Starts off the Discrete Event Simulator
 */

public class DiscreteEventSimulator {
    /**
     * Creates an EventManager to simulate the execution of events.
     * This is done by using Scanner object to take in inputs. 
     * required for a RandomGenerator, then pass those arguments to EventManager. 
     * @param args String[] 
     */
    public static void start() {
        Scanner sc = new Scanner(System.in);
        int seed = sc.nextInt();
        int numServers = sc.nextInt();
        int numWaitingSlots = sc.nextInt();
        int numCustomer = sc.nextInt();
        double arrivalRate = sc.nextDouble();
        double svcRate = sc.nextDouble();
        sc.close();
        double restRate = 1.0;

        EventManager eventManager = new EventManager(numServers, numCustomer, 
            numWaitingSlots,seed,arrivalRate,svcRate,restRate);
        eventManager.doService();
    }
}

EventManager

package cs2030.simulator;

import java.util.PriorityQueue;

/**
 * EventManager class that handles the sequence of Event executions.
 */

class EventManager {

    /**
     * Array of servers that determines the way ArrivalEvents are processed.
     */
    Server [] servers;

    /**
     * PriorityQueue of events to be cleared by the end of the simulation.
     */
    PriorityQueue<Event> events;

    /**
     * Statistics object to keep track of average waiting time of served customers,
     * number of served customers,
     * and the number of customers that left without being served.
     */
    Statistics statistics = new Statistics();

    /**
     * RandomGenerator object to randomise arrival time as well as
     * service time, which is the time to convert ServedEvent to DoneEvent.
     */
    RandomGenerator gen;

    /**
     * Constructs a EventManager that creates the ArrivalEvents of randomised time.
     * from the number of customers, load them into the PriorityQueue.
     * @param numServers the number of servers to be created.
     * @param numCustomer the number of customers to be served, which is
     * also equal to the number of ArrivalEvents preloaded onto PriorityQueue.
     * @param seed intialising value where the random values are generated from
     * @param arrivalRate arrival rate, mu in RandomGenerator constructor
     * @param svcRate service rate, lambda in RandomGenerator constructor
     * @param restRate rest rate, rho in RandomGenerator constructor
     */
    EventManager(int numServers, int numCustomer, int numWaitingSlots
        int seed, double arrivalRate, double svcRate, double restRate) {
        this.events = new PriorityQueue<>(new EventComparator());
        this.gen = new RandomGenerator(seed, arrivalRate,svcRate,restRate);
        double time = 0;
        Customer customer = new Customer(time);
        ArrivalEvent tempEvent = new ArrivalEvent(customer,time);
        events.add(tempEvent);
        for (int i = 0;i < numCustomer - 1;i++) {
            double x = gen.genInterArrivalTime();
            time += x;
            customer = new Customer(time);
            tempEvent = new ArrivalEvent(customer,time);
            events.add(tempEvent);
        }
        this.servers = new Server [numServers];
        for (int i = 0;i < numServers;i++) {
            this.servers[i] = new Server(numWaitingSlots);
        }
    }


    /**
     * Processes the full sequence of ArrivalEvents to calculate statistics.
     * This process is split into a few stages,
     * (i)At the start of each loop, get the first event from the PriorityQueue
     * (ii)prints the profile of the event to signal that we start processing it
     * (iii)current event creates the next event, 
     * with some information on the available servers as well as RandomGenerator
     * in case a DoneEvent can be created from the current event.
     * (iv) If applicable, Statistics are also updated after 
     * the creation of the new event, which will also be added to the PriorityQueue. 
     * (v) prints the statistics after the PriorityQueue is cleared.
     */
    void doService() {
        while (events.size() > 0) {
            Event firstEvent = getFirstEvent();
            System.out.println(firstEvent);
            Event newEvent = firstEvent.getNextEvent(servers,gen);
            if (newEvent != null) {
                newEvent.updateStatistics(statistics);
                events.add(newEvent);
            }

        }
        System.out.println(statistics);
    }

    /**
     * Accesses an event as well as remove it from the PriorityQueue.
     * @return the first event from the PriorityQueue, 
     * according to the Comparator object it was created with.
     */
    Event getFirstEvent() {
        return events.poll();
    }
}

EventComparator

package cs2030.simulator;

import java.util.Comparator;

/**
 * EventComparator class to create a comparison criteria for events.
 */
class EventComparator implements Comparator<Event> {

    /**
     * Compares 2 Events and decides which is smaller, equal or greater.
     * The first key is to check for the earliest time.
     * If there is a tie breaker, customerID is checked instead, 
     * which also hints on the priority of different type of events.
     * @param e1 left event
     * @param e2 right event
     * @return -1 if left event is prioritised over right event. 
     * 0 if there isn't a priority, which will not happen in this case.
     * 1 if right event is prioritised over left event.
     */
    public int compare(Event e1, Event e2)  {
        if (e1.getTime() < e2.getTime()) {
            return -1;
        } else if (e1.getTime() > e2.getTime()) {
            return 1;
        } else if (e1.getCustomerID() < e2.getCustomerID()) {
            return -1;
        } else if (e1.getCustomerID() > e2.getCustomerID()) {
            return 1;
        } else {
            System.out.println("Bug with code, please check");
            return 0;
        }

    }
}

Clienti

package cs2030.simulator;

/**
 * Customer class that holds customer information.
 */

class Customer {

    /**
     * Counter integer that generates CustomerID in a non-repetitive way.
     */
    private static int counter = 1;

    /**
     * CustomerID  that allows distinguishing between 2 customers.
     */
    private final int customerID;

    /**
     * Time when the customer first arrives.
     */
    private final double time;

    /**
     * Creates Customer.
     * @param time randomised arrival time of customer
     */

    Customer(double time) {
        this.customerID = counter;
        this.time = time;
        counter++;
    }

    int getCustomerID() {
        return this.customerID;
    }

    double getTime() {
        return this.time;
    }

}

Server

package cs2030.simulator;

import java.util.ArrayList;

/**
 * Server class that redirects the service of customers according to availability.
 * The status of customer service is seen in terms of events that involves customers.
 */
class Server {
    /**
     * Counter integer that generates ServerID in a non-repetitive way.
     */
    private static int counter = 1;
    /**
     * CustomerID  that allows distinguishing between 2 servers.
     */
    private int serverID;





    //TBC to be re-entered

    private ArrayList<Event> serverQueue = new ArrayList<>();

    private final int maxSize;

    /**
     * Creates a server.
     */
    Server(int numWaitingSlots) {
        this.serverID = counter;
        this.maxSize = 1 + numWaitingSlots;
        counter++;
    }

    int getServerID() {
        return this.serverID;
    }

    void addToQueue(Event newEvent) {
        //assert this.serverQueue.size()<=this.maxSize;
        this.serverQueue.add(newEvent);
    }

    /**
     * Checks whether the first slot inside the Server has been taken.
     * @return true if first slot has not been taken, false otherwise.
     */
    boolean canTakeServedEvent() {
        return this.serverQueue.size()==0;
    }//its quite special, only 1 value

    /**
     * Checks whether the second slot inside the Server has been taken.
     * @return true if the second slot has not been and the first slot is taken,
     * false otherwise.
     */
    boolean canTakeWaitEvent() {
        int x = this.serverQueue.size();
        return (x>0 && x<this.maxSize);
    }// == qty means false and cant put

    /**
     * Clears up the 2nd slot of the server. 
     * This is done by removing up the 2nd customer to the 1st slot.
     * and replace the 2nd slot with null status.
     */
    void flushDoneEvent() {
        //theDoneEvent should be always at the first slot
        //everything will be pushed forward and auto updated
        this.serverQueue.remove(0); //no Exception?
        //all previous assumptions of the changed state alrdy done, this just qty

    }

    /**
     * Gets the timestamp at which a customer waiting can expect to be served.
     * @return earliest possible time at which waiting customer can be served
     */


    //TBC this is really hard
    double getDoneTime() {
        return this.served.getTime();
    }
}

Statistiche

package cs2030.simulator;

/**
 * Statistics class to keep track of total waiting time of customers.
 * the number of customers who left without being served.
 * the number of customers who are served.
 */
class Statistics {
    private double waitingTime = 0;
    private int numLeft = 0;
    private int numServed = 0;

    /**
     * Creates Statistics object using the empty constructor.
     */
    Statistics(){}

    /**
     * Increases the number of customers who are served.
     */
    void increaseServed() {
        numServed++;
    }

    /**
     * Increases waiting time of customers.
     */
    void increaseWaitingTime(double time) {
        waitingTime += time;
    }

    /**
     * Increases the number of customers who left without being served.
     */
    void increaseLeft() {
        numLeft++;
    }

    /**
     * Formats the Statistics to print all information gathered.
     */
    public String toString() {
        double x = waitingTime / numServed;
        return '[' + String.format("%.3f",x) + ' ' + 
            numServed + ' ' + numLeft + ']';
    }
}

Evento

package cs2030.simulator;


/**
 * Abstract Event class to enforce polymorphism.
 * Forces its subclasses to have the ability to create the next event
 * and to update statistics.
 */
abstract class Event {
    /**
     * Customer that the event is involving.
     */
    private final Customer customer;
    /**
     * Time at which the event is created, 
     * which may differ from customer arrival time if 
     * it is only created when it is caused to wait by another preceeding event.
     */
    private final double time;

    /** 
     * Creates an Event.
     * @param customer customer that the event is involving
     * @param time time at which event is created
     */
    Event(Customer customer, double time) {
        this.customer = customer;
        this.time = time;
    }


    /** 
     * Creates the next event of parent type based on its original type.
     */
    abstract Event getNextEvent(Server [] servers,RandomGenerator gen);

    /** Modifies information in statistics if required.
     */
    abstract void updateStatistics(Statistics statistics);

    Customer getCustomer() {
        return this.customer;
    }

    int getCustomerID() {
        return this.customer.getCustomerID();
    }

    double getTime() {
        return this.time;
    }
}

ArrivalEvent

package cs2030.simulator;


/**
 * ArrivalEvent class to simulate the act of a customer arriving.
 */
class ArrivalEvent extends Event {

    /** 
     * Creates an ArrivalEvent.
     * @param customer customer that the event is involving.
     * @param time time at which event is created.
     */
    ArrivalEvent(Customer customer, double time) {
        super(customer, time);
    }

    /**
     * Creates the next event based on the availability of servers.
     * The available server will be updated to hold a field of the event 
     * and be involved in the creation of new event.
     * @param servers Array of servers to be checked
     * @param gen RandomGenerator, not used in this case.
     * @return parent class Event, which could be in the form of
     * LeaveEvent, if there are no available servers.
     * ServedEvent, if there exists an available server that is completely free.
     * WaitEvent, if there exists available server and there are no empty servers.
     * null, which won't be reached as it's a Debugging statement.
     */
    Event getNextEvent(Server [] servers,RandomGenerator gen) {
        Server freeServer = getFreeServer(servers);
        if (freeServer == null) {
            return createLeaveEvent();
        } else if (freeServer.canTakeServedEvent()) {
            ServedEvent newEvent = createServedEvent(freeServer);
            freeServer.addToQueue(newEvent);
            return newEvent;
        } else if (freeServer.canTakeWaitEvent()) {
            WaitEvent newEvent = createWaitEvent(freeServer);
            freeServer.addToQueue(newEvent);
            return newEvent;
        } else { //I think the type to reference before polymorphing is impt, dk wrapper thingy
            System.out.println("Bug in ArrivalEvents");
            return null;
        }
    }

    /**
     * Creates a LeaveEvent not bounded to any server.
     * @return LeaveEvent
     */
    LeaveEvent createLeaveEvent() {
        return new LeaveEvent(this.getCustomer(),this.getTime());
    }

    /**
     * Creates a ServedEvent bounded to an empty server.
     * @param freeServer the server that is empty.
     * @return ServedEvent.
     */
    ServedEvent createServedEvent(Server freeServer) {
        return new ServedEvent(this.getCustomer(),this.getTime(),freeServer);
    }

    /**
     * Creates a WaitEvent bounded to a partially occupied server.
     * @param freeServer the server that is partially occupied.
     * @return WaitEvent.
     */
    WaitEvent createWaitEvent(Server freeServer) {
        return new WaitEvent(this.getCustomer(),this.getTime(),freeServer);
    }

    /**
     * Modifies information in statistics if required.
     * @param statistics Not used in this case.
     */
    void updateStatistics(Statistics statistics) {
        return;
    }

    /** 
     * Finds the earliest available server based on search results.
     * @param servers Array of servers to be checked.
     * @return Server if an empty server or partially empty server is found
     * null otherwise.
     */
    Server getFreeServer(Server[] servers) {
        boolean hasFoundSlots = false;
        Server choiceServer = null;
        for (int i = 0; i < servers.length;i++) {
            Server newServer = servers[i];
            if (newServer.canTakeServedEvent()) {
                return newServer;
            } else if (newServer.canTakeWaitEvent() && !hasFoundSlots) {
                choiceServer = newServer;
                hasFoundSlots = true;
            }
        }
        if (hasFoundSlots == false) {
            return null;
        } else {
            return choiceServer;
        }
    }


    /**
     * Formats the ArrivalEvent to print out its profile.
     */
    public String toString() {
        return String.format("%.3f",this.getTime()) + ' ' +
        this.getCustomerID() + " arrives";
    }
}

LeaveEvent

package cs2030.simulator;

/**
 * LeaveEvent class to simulate the act of customer leaving without being served.
 */
class LeaveEvent extends Event {
    private Server server;

    /** 
     * Creates an LeaveEvent.
     * @param customer customer that the event is involving.
     * @param time time at which event is created.
     */
    LeaveEvent(Customer customer, double time) {
        super(customer,time);
    }

    /**
     * Creates a null object to signal no actual Event type is created.
     * @param servers Array of servers, not used in this case
     * @param gen RandomGenerator, not used in this case
     * @return null object
     */
    Event getNextEvent(Server [] servers, RandomGenerator gen) {
        return null;
    }

    /**
     * Increases the customer leave count inside statistics.
     * @param statistics statistics to be updated
     */
    void updateStatistics(Statistics statistics) {
        statistics.increaseLeft();
    }

    /**
     * Formats the LeaveEvent to print out its profile.
     */
    public String toString() {
        return String.format("%.3f",this.getTime()) +
            ' ' + this.getCustomerID() + " leaves";
    }

}

DoneEvent

package cs2030.simulator;

/**
 * DoneEvent class to simulate the completion of service to a customer by a server.
 */
class DoneEvent extends Event {
    /** 
     * Server that the DoneEvent belongs to.
     */
    private Server server;

    /** 
     * Creates an DoneEvent.
     * @param customer customer that the event is involving.
     * @param time time at which event is created.
     * @param server server that the DoneEvent belongs to.
     */
    DoneEvent(Customer customer, double time, Server server) {
        super(customer,time);
        this.server = server;
    }
    /**
     * Creates a null object to signal no actual Event type is created.
     * Server is being updated with the service time the 
     * next event should adhere to if any.
     * @param servers Array of servers, not used in this case.
     * @param gen RandomGenerator, not used in this case.
     * @return null object.
     */

    Event getNextEvent(Server [] servers,RandomGenerator gen) {
        this.server.flushDoneEvent();
        return null;
    }

    /**
     * Modifies information in statistics if required.
     * @param statistics Not used in this case 
     */
    void updateStatistics(Statistics statistics) {
        return;
    }

    /**
     * Formats the DoneEvent to print out its profile.
     */
    public String toString() {
        return String.format("%.3f",this.getTime()) +
            ' ' + this.getCustomerID() + " done serving by " +
            server.getServerID();
    }

}

WaitEvent

package cs2030.simulator;


/**
 * WaitEvent class to simulate the act of customer waiting.
 * for another customer to be served by the same server.
 */
class WaitEvent extends Event {
    /** 
     * Server that the WaitEvent belongs to.
     */
    private Server server;

    /** 
     * Creates an WaitEvent.
     * @param customer customer that the event is involving
     * @param time time at which event is created
     * @param server server that the WaitEvent belongs to
     */
    WaitEvent(Customer customer, double time, Server server) {
        super(customer,time);
        this.server = server;
    }

    /**
     * Creates a ServedEvent to signal that the current customer can now be served.
     * Timestamp the current customer is being served is taken from the server.
     * @param servers Array of servers to be checked, not used in this case
     * @param gen RandomGenerator, not used in this case
     * @return ServedEvent
     */
    ServedEvent getNextEvent(Server [] servers,RandomGenerator gen) {
        if (!this.server.canTakeWaitEvent()) {
            ServedEvent newEvent = new ServedEvent(this.getCustomer(), 
                this.server.getDoneTime(), this.server);
            this.server.setWaitEvent(newEvent);
            return newEvent;
        }
        return null;
    }

    /**
     * Modifies information in statistics if required.
     * @param statistics Not used in this case 
     */
    void updateStatistics(Statistics statistics) {
        return;
    }

    /**
     * Formats the WaitEvent to print out its profile.
     */
    public String toString() {
        return (String.format("%.3f",this.getTime()) +
            ' ' + this.getCustomerID() + " waits to be served by " +
            server.getServerID());
    }

}

ServedEvent

package cs2030.simulator;


/**
 * ServedEvent class to simulate the start of service to a customer by a server.
 */
class ServedEvent extends Event {

    /** 
     * Server that the ServedEvent belongs to.
     */
    private Server server;

    /** 
     * Creates an ServedEvent.
     * @param customer customer that the event is involving
     * @param time time at which event is created
     * @param server server that the ServedEvent belongs to
     */

    ServedEvent(Customer customer, double time, Server server) {
        super(customer,time);
        this.server = server;
    }
    /**
     * Creates a DoneEvent to signal that the service has been completed.
     * Time taken to complete the service is randomised by RandomGenerator.
     * DoneEvent is created at the new timestamp of current time of ServedEvent
     * added to the randomised service time.
     * @param servers Array of servers to be checked, not used in this case
     * @param gen RandomGenerator, to randomise service time
     * @return DoneEvent
     */

    DoneEvent getNextEvent(Server [] servers,RandomGenerator gen) {
        double x = gen.genServiceTime​();
        DoneEvent newEvent = new DoneEvent(this.getCustomer(), 
            this.getTime() + x,this.server);
        this.server.setServedEvent(newEvent);
        return newEvent;
    }

    /**
     * Increases the customer served count in statistics.
     * and increase the waiting time by the customer if any
     * @param statistics statistics to be updated
     */
    void updateStatistics(Statistics statistics) {
        statistics.increaseServed();
        statistics.increaseWaitingTime(this.getTime() - this.getCustomer().getTime());
    }

    /**
     * Formats the ServedEvent to print out its profile.
     */
    public String toString() {
        return (String.format("%.3f",this.getTime()) + ' ' +
            this.getCustomerID() + " served by " + server.getServerID());
    }
}

Il dilemma principale:

  1. Ho creato 2 classi Event e Server per completare l'assegnazione (mostrato nel link sopra). Sono stato costretto a inserire una proprietà di Server in Event in modo che gli eventi potessero stampare facilmente il profilo come parte dei criteri di assegnazione, ma sono stato anche costretto a inserire una proprietà di Event in Server , trovo che mi risparmierebbe molto lavoro per comunicare a WaitEvent in Server di che ora passare a quando si diventa ServedEvent . Si tratta di una forma di dipendenza ciclica (una cattiva pratica), e se lo è, è giustificabile farlo?

Altri dilemmi:

  1. Informazioni sul collegamento tra le responsabilità di classe e qual è il modo migliore di richiamare una chiamata di metodo tra oggetti diversi in ambiti diversi.

    EventManager è ritenuto responsabile del mantenimento di un campo Statistics e di un PriorityQueue di Events , poiché è il Manager. Nella mia implementazione avrei potuto scrivere uno dei seguenti:

    A: newEvent.updateStatistics(statistics)

    B: statistics.updateStatistics(newEvent)

    In A, per rendere un metodo per la classe Event è più facile e più accessibile in termini di lunghezza del codice. Ogni sottoclasse di Event avrà una propria versione di aggiornamento delle statistiche e quindi eviterà alcuni dispacci sul tipo delle diverse classi, che credo sia il principio di OOP se non mi sbaglio in questo.

    D'altra parte, in B sembra che sia più soddisfacente per la responsabilità. L'oggetto newEvent è il fornitore delle informazioni, quindi dovrebbe essere inserito negli argomenti.

    Ho scelto A alla fine poiché durante il tempo ho trovato B logico, mi sono reso conto che devo fare brutti dispatching e sono tentato di restituire le statistiche come argomenti agli eventi, sotto forma di newEvent.f(this) , nel metodo Statistica %codice%. Questo mi impedisce di inviare updateStatistics s come argomenti dall'inizio. Qual è la tua opinione su questo dilemma del design della responsabilità rispetto alla facilità di codifica per il programmatore?

  2. Il mio compito mi richiede di utilizzare Event object e ho deciso di implementarlo come un singleton, creato in RandomGenerator . Tuttavia, questo oggetto viene utilizzato in istanze molto specifiche in EventManager , che in realtà è il tipo di riferimento Event newEvent = firstEvent.getNextEvent(servers,gen) per gestire tutti i casi in base al polimorfismo e in realtà solo 2 su 5 devono richiamare i metodi di questo Event . Attualmente, se il problema è semplice, ho solo bisogno di passare questo oggetto singleton RandomGenerator una volta in un metodo e il 3/5 degli altri casi non lo userà. Ma se questo 2/5 casi che lo usano ha un'ulteriore ramificazione su sotto-casi, diventerebbe brutto continuare a passare questo oggetto singleton e fare in modo che i casi non coinvolti ottengano accesso ai metodi RandomGenerator ? C'è un modo per aggirare questo problema?

posta Prashin Jeevaganth 26.09.2018 - 03:39
fonte

0 risposte

Leggi altre domande sui tag