Здесь что-то вроде неизменности будет вашим другом.
@ Ner правильно , указав , что метод setPerson()
это то, что нарушает ваш код, поскольку вы создаете новый объект Person
вместо того, чтобы использовать тот, который передается методу setter, setPerson
, в классе BusTicket
.
Это приводит к потере данные в переданном Person
объекте, который необходим для расчета.
По умолчанию name
персонажа null
, символ gender
по умолчанию 'u0000'
и age
по умолчанию будет 0
. Это значение age
- это то, что ловит первый оператор if в методе вычисления класса BusTicket
каждый раз, поскольку 0
меньше 16
.
One Чтобы внести изменения, можно взять:
public void setPerson(Person p) {
p=new Person();
person=p;
}
и преобразовать его в:
public void setPerson(final Person person) {
this.person = person;
}
Обратите внимание на использование ключевого слова final
здесь. Это отличный способ защитить входящие аргументы метода от изменения их ссылки в области действия метода.
Альтернативная структура кода
Я бы предложил следующие альтернативы вашим классам, поскольку они иллюстрируют лучшие практики, которые предотвратили бы появление подобной ошибки.
import java.util.Scanner;
import java.util.function.Function;
public class Main {
public static void main(final String[] args) {
try(final Scanner scanner = new Scanner(System.in)) {
final Person person = getPersonUsing(scanner);
final BusTicket busTicket = getBusTicketForPersonUsing(scanner).apply(person);
System.out.printf("%n%s", busTicket);
}
}
private static Person getPersonUsing(final Scanner scanner) {
System.out.print("Enter the passenger name: ");
final String name = scanner.nextLine();
System.out.print("Enter the gender: ");
final Person.Gender gender = Person.Gender.valueOf(scanner.next().charAt(0));
System.out.print("Enter the age: ");
final int age = scanner.nextInt();
return new Person(name, gender, age);
}
private static Function<Person, BusTicket> getBusTicketForPersonUsing(final Scanner scanner) {
return person -> {
System.out.print("Enter the ticket number: ");
final int ticketNumber = scanner.nextInt();
System.out.print("Enter the base ticket price: ");
final float baseTicketPrice = scanner.nextFloat();
return new BusTicket(ticketNumber, baseTicketPrice, person);
};
}
}
public class Person {
private final String name;
private final Person.Gender gender;
private final int age;
public Person(
final String name,
final Person.Gender gender,
final int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public String getName() {
return this.name;
}
public Person.Gender getGender() {
return this.gender;
}
public int getAge() {
return this.age;
}
@Override
public String toString() {
return String.format(
"%s (%02d %s)",
getName(),
getAge(),
getGender().asSingleLetter());
}
public enum Gender {
FEMALE,
MALE;
public String asSingleLetter() {
return String.valueOf(name().charAt(0));
}
public static Gender valueOf(final char gender) {
switch (Character.toUpperCase(gender)) {
case 'F': return FEMALE;
case 'M': return MALE;
default:
throw new IllegalArgumentException(String.format("Gender [%s] could not be determined.", gender));
}
}
}
}
import java.util.function.Function;
public class BusTicket {
private final int ticketNumber;
private final float baseTicketPrice;
private final float totalAmount;
private final Person person;
public BusTicket(
final int ticketNumber,
final float baseTicketPrice,
final Person person) {
this.ticketNumber = ticketNumber;
this.baseTicketPrice = baseTicketPrice;
this.totalAmount = calculateTotal(person).apply(baseTicketPrice);
this.person = person;
}
public int getTicketNumber() {
return this.ticketNumber;
}
public float getBaseTicketPrice() {
return this.baseTicketPrice;
}
public float getTotalAmount() {
return this.totalAmount;
}
public Person getPerson() {
return person;
}
@Override
public String toString() {
return String.format(
"%nTicket no: %d%n" +
"Passenger: %s%n" +
"Base price of a ticket: $%.2f%n" +
"Total Amount: $%.2f%n",
getTicketNumber(),
getPerson(),
getBaseTicketPrice(),
getTotalAmount());
}
private static float calculateDiscountForPerson(final Person person) {
// 50% discount for age of 16
if(person.getAge() < 16) {
return 0.50f;
// 25% discount for age over 60
} else if(person.getAge() > 60) {
return 0.25f;
// 10% discount for females
} else if (person.getGender() == Person.Gender.FEMALE) {
return 0.10f;
// 0% discount for men between 16 and 60
} else {
return 0.00f;
}
}
private static Function<Float, Float> calculateTotal(final Person person) {
return baseTicketPrice ->
baseTicketPrice * (1.0f - calculateDiscountForPerson(person));
}
}
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import static com.shaba.Person.Gender.FEMALE;
import static com.shaba.Person.Gender.MALE;
import static org.junit.jupiter.api.Assertions.assertEquals;
class BusTicketTest {
private static final int TICKET_NUMBER = 42;
private static final float BASE_TICKET_PRICE = 100.0f;
@Test
void shouldReturnExpectedTicketPrice() {
// Map of Person and Expected Discounted Ticket Price
final Map<Person, Float> cases = new HashMap<> ();
cases.put(new Person("Tom", MALE, 15), 50.0f);
cases.put(new Person("Jen", FEMALE, 15), 50.0f);
cases.put(new Person("Tom", MALE, 4), 50.0f);
cases.put(new Person("Jen", FEMALE, 12), 50.0f);
cases.put(new Person("Tom", MALE, 61), 75.0f);
cases.put(new Person("Jen", FEMALE, 61), 75.0f);
cases.put(new Person("Tom", MALE, 99), 75.0f);
cases.put(new Person("Jen", FEMALE, 75), 75.0f);
cases.put(new Person("Jen", FEMALE, 16), 90.0f);
cases.put(new Person("Jen", FEMALE, 27), 90.0f);
cases.put(new Person("Jen", FEMALE, 48), 90.0f);
cases.put(new Person("Jen", FEMALE, 60), 90.0f);
cases.put(new Person("Tom", MALE, 16), 100.0f);
cases.put(new Person("Tom", MALE, 27), 100.0f);
cases.put(new Person("Tom", MALE, 48), 100.0f);
cases.put(new Person("Tom", MALE, 60), 100.0f);
cases.forEach(this::assertExpectedTotalTicketPrice);
}
private void assertExpectedTotalTicketPrice(
final Person person,
final float expectedTotal) {
assertEquals(expectedTotal, makeTestBusTicket().apply(person).getTotalAmount());
}
private Function<Person, BusTicket> makeTestBusTicket() {
return person -> new BusTicket(TICKET_NUMBER, BASE_TICKET_PRICE, person);
}
}
Тестовые случаи
Эти тестовые случаи теперь происходят, как и ожидалось.
+-----|--------|-------------------|-----------------------------+
| Age | Gender | Expected Discount | Expected total ticket price |
|-----|--------|-------------------|-----------------------------|
| 15 | M | 0.50 | $50.00 |
| 15 | F | 0.50 | $50.00 |
| 4 | M | 0.50 | $50.00 |
| 12 | F | 0.50 | $50.00 |
|-----|--------|-------------------|-----------------------------|
| 61 | M | 0.25 | $75.00 |
| 61 | F | 0.25 | $75.00 |
| 99 | M | 0.25 | $75.00 |
| 75 | F | 0.25 | $75.00 |
|-----|--------|-------------------|-----------------------------|
| 16 | F | 0.10 | $90.00 |
| 27 | F | 0.10 | $90.00 |
| 48 | F | 0.10 | $90.00 |
| 60 | F | 0.10 | $90.00 |
|-----|--------|-------------------|-----------------------------|
| 16 | M | 0.00 | $100.00 |
| 27 | M | 0.00 | $100.00 |
| 48 | M | 0.00 | $100.00 |
| 60 | M | 0.00 | $100.00 |
+-----|--------|-------------------|-----------------------------+
Пример вывода
Enter the passenger name: Tom
Enter the gender: M
Enter the age: 98
Enter the ticket number: 2346
Enter the base ticket price: 100
Ticket no: 2346
Passenger: Tom (98 M)
Base price of a ticket: $100.00
Total Amount: $75.00