Difference between revisions of "Visitor (padrão de desenho)/Exercício 1: Cálculo de Impostos"

From Wiki**3

< Visitor (padrão de desenho)
(Problem)
 
(One intermediate revision by the same user not shown)
Line 18: Line 18:
 
Crie um exemplo (main) que ilustre a aplicação das diferentes classes.
 
Crie um exemplo (main) que ilustre a aplicação das diferentes classes.
  
= Solution =
+
= Solução =
  
 
== Taxpayer Classes ==
 
== Taxpayer Classes ==
  
 
{{CollapsedCode|Ficheiro '''Taxpayer.java'''|
 
{{CollapsedCode|Ficheiro '''Taxpayer.java'''|
<java5>
+
<source lang="java">
 
/**
 
/**
 
  * Basic taxpayer.
 
  * Basic taxpayer.
 
  */
 
  */
 
public abstract class Taxpayer {
 
public abstract class Taxpayer {
/**
+
    /**
* No actual value is returned in this case.
+
    * No actual value is returned in this case.
*  
+
    *
* @param irs
+
    * @param irs
*            the visitor used to compute the revenue.
+
    *            the visitor used to compute the revenue.
* @return tax payed by this taxpayer.
+
    * @return tax payed by this taxpayer.
*/
+
    */
public double accept(FriendlyIRS irs) {
+
    public double accept(FriendlyIRS irs) {
throw new UnsupportedOperationException();
+
        throw new UnsupportedOperationException();
}
+
    }
 
}
 
}
</java5>
+
</source>
 
}}
 
}}
  
 
{{CollapsedCode|Ficheiro '''Person.java'''|
 
{{CollapsedCode|Ficheiro '''Person.java'''|
<java5>
+
<source lang="java">
 
/**
 
/**
 
  * Individual taxpayer.
 
  * Individual taxpayer.
  *  
+
  *
 
  * We omitted the initialisation code.
 
  * We omitted the initialisation code.
 
  */
 
  */
 
public class Person extends Taxpayer {
 
public class Person extends Taxpayer {
/**
+
    /**
* @see Taxpayer#accept(FriendlyIRS)
+
    * @see Taxpayer#accept(FriendlyIRS)
*/
+
    */
@Override
+
    @Override
public double accept(FriendlyIRS irs) {
+
    public double accept(FriendlyIRS irs) {
return irs.taxPerson(this);
+
        return irs.taxPerson(this);
}
+
    }
 
}
 
}
</java5>
+
</source>
 
}}
 
}}
  
 
{{CollapsedCode|Ficheiro '''Company.java'''|
 
{{CollapsedCode|Ficheiro '''Company.java'''|
<java5>
+
<source lang="java">
 
import java.util.ArrayList;
 
import java.util.ArrayList;
  
 
/**
 
/**
 
  * A company has employees (persons).
 
  * A company has employees (persons).
  *  
+
  *
 
  * We omitted the initialisation code.
 
  * We omitted the initialisation code.
 
  */
 
  */
 
public class Company extends Taxpayer {
 
public class Company extends Taxpayer {
/**
+
    /**
* The employees in this company.
+
    * The employees in this company.
*/
+
    */
private ArrayList<Person> _employees = new ArrayList<Person>();
+
    private ArrayList<Person> _employees = new ArrayList<Person>();
  
/**
+
    /**
* Simple constructor for initialising the company with some employees.
+
    * Simple constructor for initialising the company with some employees.
*/
+
    */
public Company() {
+
    public Company() {
int count = (int) (Math.random() * 100);
+
        int count = (int) (Math.random() * 100);
for (int i = 0; i < count; i++)
+
        for (int i = 0; i < count; i++)
_employees.add(new Person());
+
            _employees.add(new Person());
}
+
    }
  
/**
+
    /**
* @return size of company (number of employees).
+
    * @return size of company (number of employees).
*/
+
    */
public int size() {
+
    public int size() {
return _employees.size();
+
        return _employees.size();
}
+
    }
  
/**
+
    /**
* @param index
+
    * @param index
* @return an employee
+
    * @return an employee
*/
+
    */
public Person getEmployee(int index) {
+
    public Person getEmployee(int index) {
return _employees.get(index);
+
        return _employees.get(index);
}
+
    }
  
/**
+
    /**
* @see Taxpayer#accept(FriendlyIRS)
+
    * @see Taxpayer#accept(FriendlyIRS)
*/
+
    */
@Override
+
    @Override
public double accept(FriendlyIRS irs) {
+
    public double accept(FriendlyIRS irs) {
return irs.taxCompany(this);
+
        return irs.taxCompany(this);
}
+
    }
 
}
 
}
</java5>
+
</source>
 
}}
 
}}
  
 
{{CollapsedCode|Ficheiro '''Region.java'''|
 
{{CollapsedCode|Ficheiro '''Region.java'''|
<java5>
+
<source lang="java">
 
import java.util.ArrayList;
 
import java.util.ArrayList;
  
 
/**
 
/**
 
  * A region has companies.
 
  * A region has companies.
  *  
+
  *
 
  * We omitted the initialisation code.
 
  * We omitted the initialisation code.
 
  */
 
  */
 
public class Region extends Taxpayer {
 
public class Region extends Taxpayer {
/**
+
    /**
* The companies in this region.
+
    * The companies in this region.
*/
+
    */
private ArrayList<Company> _companies = new ArrayList<Company>();
+
    private ArrayList<Company> _companies = new ArrayList<Company>();
  
/**
+
    /**
* Simple constructor for initialising the region with some companies.
+
    * Simple constructor for initialising the region with some companies.
*/
+
    */
public Region() {
+
    public Region() {
int count = (int) (Math.random() * 100);
+
        int count = (int) (Math.random() * 100);
for (int i = 0; i < count; i++)
+
        for (int i = 0; i < count; i++)
_companies.add(new Company());
+
            _companies.add(new Company());
}
+
    }
  
/**
+
    /**
* @return size of region (number of companies).
+
    * @return size of region (number of companies).
*/
+
    */
public int size() {
+
    public int size() {
return _companies.size();
+
        return _companies.size();
}
+
    }
  
/**
+
    /**
* @param index
+
    * @param index
* @return a company
+
    * @return a company
*/
+
    */
public Company getCompany(int index) {
+
    public Company getCompany(int index) {
return _companies.get(index);
+
        return _companies.get(index);
}
+
    }
  
/**
+
    /**
* @see Taxpayer#accept(FriendlyIRS)
+
    * @see Taxpayer#accept(FriendlyIRS)
*/
+
    */
@Override
+
    @Override
public double accept(FriendlyIRS irs) {
+
    public double accept(FriendlyIRS irs) {
return irs.taxRegion(this);
+
        return irs.taxRegion(this);
}
+
    }
 
}
 
}
</java5>
+
</source>
 
}}
 
}}
  
Line 166: Line 166:
  
 
{{CollapsedCode|Ficheiro '''FriendlyIRS.java'''|
 
{{CollapsedCode|Ficheiro '''FriendlyIRS.java'''|
<java5>
+
<source lang="java">
 
/**
 
/**
 
  * The IRS computing visitor interface.
 
  * The IRS computing visitor interface.
 
  */
 
  */
 
public abstract class FriendlyIRS {
 
public abstract class FriendlyIRS {
/**
+
    /**
* @param person
+
    * @param person
* @return tax payed by this person.
+
    * @return tax payed by this person.
*/
+
    */
public abstract double taxPerson(Person person);
+
    public abstract double taxPerson(Person person);
  
/**
+
    /**
* @param company
+
    * @param company
* @return tax payed by this company.
+
    * @return tax payed by this company.
*/
+
    */
public abstract double taxCompany(Company company);
+
    public abstract double taxCompany(Company company);
  
/**
+
    /**
* @param region
+
    * @param region
* @return tax payed by this region.
+
    * @return tax payed by this region.
*/
+
    */
public abstract double taxRegion(Region region);
+
    public abstract double taxRegion(Region region);
 
}
 
}
</java5>
+
</source>
 
}}
 
}}
  
Line 195: Line 195:
  
 
{{CollapsedCode|Ficheiro '''VanillaTaxes.java'''|
 
{{CollapsedCode|Ficheiro '''VanillaTaxes.java'''|
<java5>
+
<source lang="java">
 
/**
 
/**
 
  * Usual tax calculator.
 
  * Usual tax calculator.
Line 201: Line 201:
 
public class VanillaTaxes extends FriendlyIRS {
 
public class VanillaTaxes extends FriendlyIRS {
  
/**
+
    /**
* @see FriendlyIRS#taxCompany(Company)
+
    * @see FriendlyIRS#taxCompany(Company)
*/
+
    */
@Override
+
    @Override
public double taxCompany(Company company) {
+
    public double taxCompany(Company company) {
double tax = 0;
+
        double tax = 0;
for (int index = 0; index < company.size(); index++)
+
        for (int index = 0; index < company.size(); index++)
tax += company.getEmployee(index).accept(this);
+
            tax += company.getEmployee(index).accept(this);
return tax;
+
        return tax;
}
+
    }
  
/**
+
    /**
* @see FriendlyIRS#taxPerson(Person)
+
    * @see FriendlyIRS#taxPerson(Person)
*/
+
    */
@Override
+
    @Override
public double taxPerson(Person person) {
+
    public double taxPerson(Person person) {
return 1;
+
        return 1;
}
+
    }
  
/**
+
    /**
* @see FriendlyIRS#taxRegion(Region)
+
    * @see FriendlyIRS#taxRegion(Region)
*/
+
    */
@Override
+
    @Override
public double taxRegion(Region region) {
+
    public double taxRegion(Region region) {
double tax = 0;
+
        double tax = 0;
for (int index = 0; index < region.size(); index++)
+
        for (int index = 0; index < region.size(); index++)
tax += region.getCompany(index).accept(this);
+
            tax += region.getCompany(index).accept(this);
return tax;
+
        return tax;
}
+
    }
  
 
}
 
}
</java5>
+
</source>
 
}}
 
}}
  
Line 238: Line 238:
  
 
{{CollapsedCode|Ficheiro '''BecauseWeCare.java'''|
 
{{CollapsedCode|Ficheiro '''BecauseWeCare.java'''|
<java5>
+
<source lang="java">
 
/**
 
/**
 
  * The discount tax system.
 
  * The discount tax system.
Line 244: Line 244:
 
public class BecauseWeCare extends FriendlyIRS {
 
public class BecauseWeCare extends FriendlyIRS {
  
/**
+
    /**
* Low water marker for revenue.
+
    * Low water marker for revenue.
*/
+
    */
private final int LOW = 1;
+
    private final int LOW = 1;
  
/**
+
    /**
* Low water marker for population.
+
    * Low water marker for population.
*/
+
    */
private final int POP = 50;
+
    private final int POP = 50;
  
/**
+
    /**
* @see FriendlyIRS#taxCompany(Company)
+
    * @see FriendlyIRS#taxCompany(Company)
*/
+
    */
@Override
+
    @Override
public double taxCompany(Company company) {
+
    public double taxCompany(Company company) {
double tax = 0;
+
        double tax = 0;
for (int index = 0; index < company.size(); index++)
+
        for (int index = 0; index < company.size(); index++)
tax += company.getEmployee(index).accept(this);
+
            tax += company.getEmployee(index).accept(this);
if (company.size() < POP || tax < LOW)
+
        if (company.size() < POP || tax < LOW)
tax *= .9;
+
            tax *= .9;
return tax;
+
        return tax;
}
+
    }
  
/**
+
    /**
* In this case, we chose not to test the LOW level (simply because we know
+
    * In this case, we chose not to test the LOW level (simply because we know
* the value is the same).
+
    * the value is the same).
*  
+
    *
* @see FriendlyIRS#taxPerson(Person)
+
    * @see FriendlyIRS#taxPerson(Person)
*/
+
    */
@Override
+
    @Override
public double taxPerson(Person person) {
+
    public double taxPerson(Person person) {
return 1;
+
        return 1;
}
+
    }
  
/**
+
    /**
* @see FriendlyIRS#taxRegion(Region)
+
    * @see FriendlyIRS#taxRegion(Region)
*/
+
    */
@Override
+
    @Override
public double taxRegion(Region region) {
+
    public double taxRegion(Region region) {
double tax = 0;
+
        double tax = 0;
for (int index = 0; index < region.size(); index++)
+
        for (int index = 0; index < region.size(); index++)
tax += region.getCompany(index).accept(this);
+
            tax += region.getCompany(index).accept(this);
if (region.size() < POP || tax < LOW)
+
        if (region.size() < POP || tax < LOW)
tax *= .9;
+
            tax *= .9;
return tax;
+
        return tax;
}
+
    }
  
 
}
 
}
</java5>
+
</source>
 
}}
 
}}
  
Line 300: Line 300:
  
 
{{CollapsedCode|Ficheiro '''App.java'''|
 
{{CollapsedCode|Ficheiro '''App.java'''|
<java5>
+
<source lang="java">
 
public class App {
 
public class App {
  
/**
+
    /**
* @param args
+
    * @param args
*/
+
    */
public static void main(String[] args) {
+
    public static void main(String[] args) {
Taxpayer c1 = new Company();
+
        Taxpayer c1 = new Company();
Taxpayer r1 = new Region();
+
        Taxpayer r1 = new Region();
Taxpayer p1 = new Person();
+
        Taxpayer p1 = new Person();
+
       
FriendlyIRS vt = new VanillaTaxes();
+
        FriendlyIRS vt = new VanillaTaxes();
FriendlyIRS ct = new BecauseWeCare();
+
        FriendlyIRS ct = new BecauseWeCare();
+
       
System.out.println("Company taxes (vanilla): " + c1.accept(vt));
+
        System.out.println("Company taxes (vanilla): " + c1.accept(vt));
System.out.println("Region taxes (vanilla): " + r1.accept(vt));
+
        System.out.println("Region taxes (vanilla): " + r1.accept(vt));
System.out.println("Person taxes (vanilla): " + p1.accept(vt));
+
        System.out.println("Person taxes (vanilla): " + p1.accept(vt));
  
System.out.println("Company taxes (care): " + c1.accept(ct));
+
        System.out.println("Company taxes (care): " + c1.accept(ct));
System.out.println("Region taxes (care): " + r1.accept(ct));
+
        System.out.println("Region taxes (care): " + r1.accept(ct));
System.out.println("Person taxes (care): " + p1.accept(ct));
+
        System.out.println("Person taxes (care): " + p1.accept(ct));
}
+
    }
  
 
}
 
}
</java5>
+
</source>
 
}}
 
}}
  

Latest revision as of 20:52, 8 November 2018

Problema

Uma agência de cobrança de impostos tornou-se famosa por permitir adaptar métodos de cobrança de impostos de forma muito flexível. Os contribuintes (Taxpayer) são pessoas (Person), empresas (Company) e regiões (Region). As pessoas trabalham nas empresas e as empresas estão sediadas em regiões. Todos têm rendimentos (as pessoas e as regiões a partir das empresas e as empresas a partir de pessoas, regiões e outras empresas) e todos devem pagar os seus impostos.

O sucesso da agência advém da facilidade com que cria variações do seu produto, o premiado FriendlyIRS. Estas aplicações permitem interrogar cada contribuinte de forma adaptada, tanto ao contribuinte, como à situação de cobrança desejada.

Assim, além da cobrança em habitual, através do seu produto VanillaTaxes, permite ainda cobrar menos impostos se o rendimento for menor que um dado valor ou se a população (número de contribuintes numa empresa ou numa região) for inferior a um dado valor de referência. Para isso, disponibiliza o seu produto BecauseWeCare.

  • A versão implementada por VanillaTaxes funciona por cobrança acumulada. Cada pessoa paga uma unidade monetária e cada empresa paga o equivalente à soma das contribuições individuais dos seus empregados. Cada região paga o equivalente à soma das contribuições das empresas nela sediadas.
  • A versão implementada por BecauseWeCare é como a anterior, mas considera ainda se o rendimento apurado é inferior a limiares de referência (BecauseWeCare.LOW, para o rendimento mínimo, mas apenas para pessoas e empresas; e BecauseWeCare.POP, para a população mínima, apenas para regiões), aplicando desconto de 10% se se verificar algum limiar.

Implemente todas as classes mencionadas:

  • Implemente esquematicamente as classes Taxpayer, Person, Company e Region, i.e., considere apenas as características estritamente necessárias para resolução do problema. As relações estruturais entre as classes devem ser explicitadas (atributos), mas não é necessário explicitar relações funcionais (métodos, interacção, etc.).
  • Implemente completamente as classes FriendlyIRS, VanillaTaxes e BecauseWeCare.

Crie um exemplo (main) que ilustre a aplicação das diferentes classes.

Solução

Taxpayer Classes

Ficheiro Taxpayer.java
/**
 * Basic taxpayer.
 */
public abstract class Taxpayer {
    /**
     * No actual value is returned in this case.
     *
     * @param irs
     *            the visitor used to compute the revenue.
     * @return tax payed by this taxpayer.
     */
    public double accept(FriendlyIRS irs) {
        throw new UnsupportedOperationException();
    }
}
Ficheiro Person.java
/**
 * Individual taxpayer.
 *
 * We omitted the initialisation code.
 */
public class Person extends Taxpayer {
    /**
     * @see Taxpayer#accept(FriendlyIRS)
     */
    @Override
    public double accept(FriendlyIRS irs) {
        return irs.taxPerson(this);
    }
}
Ficheiro Company.java
import java.util.ArrayList;

/**
 * A company has employees (persons).
 *
 * We omitted the initialisation code.
 */
public class Company extends Taxpayer {
    /**
     * The employees in this company.
     */
    private ArrayList<Person> _employees = new ArrayList<Person>();

    /**
     * Simple constructor for initialising the company with some employees.
     */
    public Company() {
        int count = (int) (Math.random() * 100);
        for (int i = 0; i < count; i++)
            _employees.add(new Person());
    }

    /**
     * @return size of company (number of employees).
     */
    public int size() {
        return _employees.size();
    }

    /**
     * @param index
     * @return an employee
     */
    public Person getEmployee(int index) {
        return _employees.get(index);
    }

    /**
     * @see Taxpayer#accept(FriendlyIRS)
     */
    @Override
    public double accept(FriendlyIRS irs) {
        return irs.taxCompany(this);
    }
}
Ficheiro Region.java
import java.util.ArrayList;

/**
 * A region has companies.
 *
 * We omitted the initialisation code.
 */
public class Region extends Taxpayer {
    /**
     * The companies in this region.
     */
    private ArrayList<Company> _companies = new ArrayList<Company>();

    /**
     * Simple constructor for initialising the region with some companies.
     */
    public Region() {
        int count = (int) (Math.random() * 100);
        for (int i = 0; i < count; i++)
            _companies.add(new Company());
    }

    /**
     * @return size of region (number of companies).
     */
    public int size() {
        return _companies.size();
    }

    /**
     * @param index
     * @return a company
     */
    public Company getCompany(int index) {
        return _companies.get(index);
    }

    /**
     * @see Taxpayer#accept(FriendlyIRS)
     */
    @Override
    public double accept(FriendlyIRS irs) {
        return irs.taxRegion(this);
    }
}

Tax Computation (Visitors)

The abstract class representing the concept of tax computation.

Ficheiro FriendlyIRS.java
/**
 * The IRS computing visitor interface.
 */
public abstract class FriendlyIRS {
    /**
     * @param person
     * @return tax payed by this person.
     */
    public abstract double taxPerson(Person person);

    /**
     * @param company
     * @return tax payed by this company.
     */
    public abstract double taxCompany(Company company);

    /**
     * @param region
     * @return tax payed by this region.
     */
    public abstract double taxRegion(Region region);
}

Simple tax computation.

Ficheiro VanillaTaxes.java
/**
 * Usual tax calculator.
 */
public class VanillaTaxes extends FriendlyIRS {

    /**
     * @see FriendlyIRS#taxCompany(Company)
     */
    @Override
    public double taxCompany(Company company) {
        double tax = 0;
        for (int index = 0; index < company.size(); index++)
            tax += company.getEmployee(index).accept(this);
        return tax;
    }

    /**
     * @see FriendlyIRS#taxPerson(Person)
     */
    @Override
    public double taxPerson(Person person) {
        return 1;
    }

    /**
     * @see FriendlyIRS#taxRegion(Region)
     */
    @Override
    public double taxRegion(Region region) {
        double tax = 0;
        for (int index = 0; index < region.size(); index++)
            tax += region.getCompany(index).accept(this);
        return tax;
    }

}

Discounted taxes.

Ficheiro BecauseWeCare.java
/**
 * The discount tax system.
 */
public class BecauseWeCare extends FriendlyIRS {

    /**
     * Low water marker for revenue.
     */
    private final int LOW = 1;

    /**
     * Low water marker for population.
     */
    private final int POP = 50;

    /**
     * @see FriendlyIRS#taxCompany(Company)
     */
    @Override
    public double taxCompany(Company company) {
        double tax = 0;
        for (int index = 0; index < company.size(); index++)
            tax += company.getEmployee(index).accept(this);
        if (company.size() < POP || tax < LOW)
            tax *= .9;
        return tax;
    }

    /**
     * In this case, we chose not to test the LOW level (simply because we know
     * the value is the same).
     *
     * @see FriendlyIRS#taxPerson(Person)
     */
    @Override
    public double taxPerson(Person person) {
        return 1;
    }

    /**
     * @see FriendlyIRS#taxRegion(Region)
     */
    @Override
    public double taxRegion(Region region) {
        double tax = 0;
        for (int index = 0; index < region.size(); index++)
            tax += region.getCompany(index).accept(this);
        if (region.size() < POP || tax < LOW)
            tax *= .9;
        return tax;
    }

}

Example Application

Simple demo application.

Ficheiro App.java
public class App {

    /**
     * @param args
     */
    public static void main(String[] args) {
        Taxpayer c1 = new Company();
        Taxpayer r1 = new Region();
        Taxpayer p1 = new Person();
        
        FriendlyIRS vt = new VanillaTaxes();
        FriendlyIRS ct = new BecauseWeCare();
        
        System.out.println("Company taxes (vanilla): " + c1.accept(vt));
        System.out.println("Region taxes (vanilla): " + r1.accept(vt));
        System.out.println("Person taxes (vanilla): " + p1.accept(vt));

        System.out.println("Company taxes (care): " + c1.accept(ct));
        System.out.println("Region taxes (care): " + r1.accept(ct));
        System.out.println("Person taxes (care): " + p1.accept(ct));
    }

}

Compiling and Running

Compiling

 javac App.java

Running

 java App

Sample outputs:

 Company taxes (vanilla): 21.0
 Region taxes (vanilla): 4631.0
 Person taxes (vanilla): 1.0
 Company taxes (care): 18.900000000000002
 Region taxes (care): 4505.800000000001
 Person taxes (care): 1.0

Region has some companies with less than the minimum number of employees:

 Company taxes (vanilla): 81.0
 Region taxes (vanilla): 192.0
 Person taxes (vanilla): 1.0
 Company taxes (care): 81.0
 Region taxes (care): 172.8
 Person taxes (care): 1.0