Uma revista deseja automatizar seu serviço de assinaturas e para isso precisa gerenciar a situação dos assinantes que realizam pagamentos por boleto. Uma assinatura pode estar “ativa” quando o pagamento está regularizado; “em atraso” durante os cinco dias corridos após o vencimento do boleto de pagamento; “em recuperação” entre 5 e 10 dias corridos após o vencimento do boleto, quando é feita a tentativa de regularização do pagamento junto ao assinante; “inadimplente” quando não há pagamento mesmo após 10 dias do vencimento e “suspensa” quando a interrupção da assinatura é solicitada pelo cliente. Exceto quando a assinatura está suspensa, sempre que o pagamento é realizado a situação dela muda para o estado “ativa”. Quando o pagamento ocorre após o vencimento, são cobrados juros de 10% sobre o valor base da assinatura. Caso esse pagamento seja feito durante a recuperação, os juros podem ser menores em função da negociação com o assinante. Em caso de inadimplência, são cobrados juros de 10% mais mora de 0,2 ao dia sobre o valor base e no caso de interrupção da assinatura, um valor proporcional pode ser cobrado. O conteúdo continuará sendo disponibilizado ao assinante enquanto a assinatura estiver ativa. Uma interface Payment define a operação calculateSubscriptionValue, que recebe como parâmetro o valor base da assinatura e retorna, dependendo da situação da assinatura, o valor que deve ser pago. As classes InterestChargesPayment, ProportionalPayment e DailyInterestPayment implementam Payment e sua operação e representam as possíveis formas de definir o valor a pagar da assinatura. A classe Subscription armazena o valor da assinatura e a data de vencimento estipulada, além de possuir as operações sendContent, que verifica o conteúdo disponibilizado, suspendSubscription quando é necessário que a assinatura seja interrompida e verifyPayment que verifica como está a situação do pagamento.
package strategy;
public class InterestChargesPaymentPolicy implements PaymentPolicy {
// Método de cálculo do valor onde é definido um valor base de juros
public float calculateSubscriptionValue(Subscription context){
return context.getPlanValue() + context.getInterest()* context.getPlanValue();
}
}
package strategy;
import java.util.Date;
public class Client {
public static void main(String[] args) {
// Payment Policies setup
Subscription.policiesSetup(
new DailyInterestPaymentPolicy(),
new InterestChargesPaymentPolicy(),
new ProportionalPaymentPolicy()
);
// Cria uma nova assinatura com a data de vencimento no dia atual do mês.
Subscription subscription = new Subscription(29.00f, new Date(2020, 7, 6));
System.out.println("\nAfter subscription start:");
System.out.println("Total to pay: " + subscription.calculateSubscriptionValue());
System.out.println("Receiving content? " + subscription.sendContent());
System.out.println("\nDay after due date:");
subscription.setInterestPaymentPolicy(0.1f);
System.out.println("Total to pay: " + subscription.calculateSubscriptionValue());
System.out.println("Receiving content? " + subscription.sendContent());
System.out.println("\n6 days after due date:");
subscription.setInterestPaymentPolicy(0.05f);
System.out.println("Total to pay: " + subscription.calculateSubscriptionValue());
System.out.println("Receiving content? " + subscription.sendContent());
System.out.println("\n11 days after due date:");
subscription.setDailyInterestPaymentPolicy(new Date(2020, 7, 17)); // colocar data aqui
System.out.println("Total to pay: " + subscription.calculateSubscriptionValue());
System.out.println("Receiving content? " + subscription.sendContent());
System.out.println("\nAfter suspending subscription:");
subscription.suspendSubscription(new Date(2020, 7, 25));
System.out.println("Total to pay: " + subscription.calculateSubscriptionValue());
System.out.println("Receiving content? " + subscription.sendContent());
// Após a realização do pagamento é definido o novo prazo
subscription.setDueDay(new Date(2020, 8, 6));
subscription.activateSubscription();
}
}
package strategy;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class ProportionalPaymentPolicy implements PaymentPolicy {
// Pagamento proporcional à quantidade de dias entre a suspensão da assinatura
// e a data de vencimento.
public float calculateSubscriptionValue(Subscription context){
// Cálculo da quantidade de dias
long totalDays = context.getSuspensionDate().getTime() - context.getDueDay().getTime();
totalDays = TimeUnit.DAYS.convert(totalDays, TimeUnit.MILLISECONDS);
return context.getPlanValue() + 0.1f* context.getPlanValue() *totalDays;
}
}
package strategy;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class DailyInterestPaymentPolicy implements PaymentPolicy {
// Método de cálculo do valor onde é aplicada multa por dia de atraso
public float calculateSubscriptionValue(Subscription context){
// Cálculo da quantidade de dias de atraso
long totalDays = context.getToday().getTime() - context.getDueDay().getTime();
totalDays = TimeUnit.DAYS.convert(totalDays, TimeUnit.MILLISECONDS);
return context.getPlanValue() + 0.1f* context.getPlanValue() + 0.2f*totalDays;
}
}
package strategy;
public interface PaymentPolicy {
// Interface que define a operação de cálculo do valor
float calculateSubscriptionValue(Subscription context);
}
package strategy;
import java.util.Date;
public class Subscription {
private final float planValue;
private Date dueDay;
private float interest;
private Date suspensionDate;
private Date today;
private static PaymentPolicy dailyInterestPaymentPolicy;
private static PaymentPolicy interestChargesPaymentPolicy;
private static PaymentPolicy proportionalPaymentPolicy;
// No construtor, são definidos o valor da assinatura e a data de vencimento do pagamento.
public Subscription(float planValue, Date dueDay){
this.dueDay = dueDay;
this.planValue = planValue;
this.interest = 0;
this.suspensionDate = null;
this.today = null;
}
public static void policiesSetup(
PaymentPolicy _dailyInterestPaymentPolicy,
PaymentPolicy _interestChargesPaymentPolicy,
PaymentPolicy _proportionalPaymentPolicy
) {
dailyInterestPaymentPolicy = _dailyInterestPaymentPolicy;
interestChargesPaymentPolicy = _interestChargesPaymentPolicy;
proportionalPaymentPolicy = _proportionalPaymentPolicy;
}
public boolean sendContent(){
return this.verifyPayment() == null;
}
// Modificadores de situação
public void activateSubscription() {
this.interest = 0;
this.suspensionDate = null;
this.today = null;
}
public void setInterestPaymentPolicy(float interest) {
this.interest = interest;
this.suspensionDate = null;
this.today = null;
}
public void setDailyInterestPaymentPolicy(Date today){
this.today = today;
this.suspensionDate = null;
this.interest = 0;
}
public void suspendSubscription(Date today){
this.suspensionDate = today;
this.today = null;
this.interest = 0;
}
private PaymentPolicy verifyPayment(){
if(this.interest != 0)
return interestChargesPaymentPolicy;
if(this.suspensionDate != null)
return proportionalPaymentPolicy;
if(this.today != null)
return dailyInterestPaymentPolicy;
return null;
}
public float calculateSubscriptionValue() {
PaymentPolicy paymentPolicy = this.verifyPayment();
if(paymentPolicy == null)
return this.planValue;
return paymentPolicy.calculateSubscriptionValue(this);
}
// Getters & Setters
public float getPlanValue() {
return planValue;
}
public float getInterest() {
return interest;
}
public Date getSuspensionDate() {
return suspensionDate;
}
public void setSuspensionDate(Date suspensionDate) {
this.suspensionDate = suspensionDate;
}
public Date getDueDay() {
return dueDay;
}
public void setDueDay(Date dueDay) {
this.dueDay = dueDay;
}
public Date getToday() {
return today;
}
public void setToday(Date today) {
this.today = today;
}
}
Strategy (Assembler) Define uma interface comum para os possíveis algoritmos, sendo usado por Context para chamar o algoritmo definido em uma instância concreta.
ConcreteStrategy (SedanCar, HatchCar) Implementa os algoritmos definidos pela interface Strategy.
Context (Car) Mantém uma referência para um objeto Strategy.
package strategy;
public interface Assembler {
//Definição das operações abstratas executáveis pelo algoritmo
public abstract void buildCarBodyWork();
public abstract void paintCar();
public abstract void mountCarParts();
}
package strategy;
public class Car {
private Assembler carAssembler;
//Método que executa as etapas de Strategy
public void manufacture(){
carAssembler.buildCarBodyWork();
carAssembler.paintCar();
carAssembler.mountCarParts();
}
public void setCarAssembler(Assembler carAssembler) {
this.carAssembler = carAssembler;
}
}
package strategy;
public class Client {
public static void main(String[] args) {
Car car = new Car();
//Instancia uma classe para a execução de um algoritmo SedanCar e passa essa instância para a instância de Car.
SedanCar sedanCar = new SedanCar();
car.setCarAssembler(sedanCar);
car.manufacture();
System.out.println("Car info:");
System.out.println(sedanCar.getCarInfo());
}
}
package strategy;
public class HatchCar implements Assembler {
// Atributos de Car que serão modificados durante as etapas do algoritmo.
private String color;
private int bodyWorkVolumesNumber;
private String engineType;
private String tireType;
private String starterMotorType;
// Implementações específicas para cada etapa do algoritmo que constrói um carro Hatch
public void buildCarBodyWork(){
this.bodyWorkVolumesNumber = 2;
}
public void paintCar() {
this.color = "blue";
}
public void mountCarParts(){
this.engineType = "V type";
this.tireType = "All Season";
this.starterMotorType = "Direct Drive";
}
public String getCarInfo(){
return "Number of bodywork volumes: " + this.bodyWorkVolumesNumber
+ "\nCar color: " + this.color
+ "\nEngine type: " + this.engineType
+ "\nTire type: " + this.tireType
+ "\nStarter motor type: " + this.starterMotorType;
}
}
package strategy;
public class SedanCar implements Assembler {
// Atributos de Car que serão modificados durante as etapas do algoritmo.
private String color;
private int bodyWorkVolumesNumber;
private String engineType;
private String tireType;
private String starterMotorType;
// Implementações específicas para cada etapa do algoritmo que constrói um carro Hatch
public void buildCarBodyWork(){
this.bodyWorkVolumesNumber = 3;
}
public void paintCar() {
this.color = "black";
}
public void mountCarParts(){
this.engineType = "W type";
this.tireType = "Summer";
this.starterMotorType = "Planetary Gear";
}
public String getCarInfo(){
return "Number of bodywork volumes: " + this.bodyWorkVolumesNumber
+ "\nCar color: " + this.color
+ "\nEngine type: " + this.engineType
+ "\nTire type: " + this.tireType
+ "\nStarter motor type: " + this.starterMotorType;
}
}