← Abstract factory lesson
Lab

Abstract factory

Sign in to run your code

Read the brief and the starter here. With a free account you can edit it, run the sample, step through it and check your work, and your progress is kept.

type TravelDate = { year: number; month: number; day: number };
type Booking = {
  id: string;
  locale: string;
  cents: number;
  date: TravelDate;
};

// The team's setting: the locale for customers whose own locale has no
// formats yet. The checker changes defaultLocale between cases.
const settings = { defaultLocale: 'en' };

// Two product roles: a price format and a date format.
interface MoneyFormat {
  format(cents: number): string;
}
interface DateFormat {
  format(date: TravelDate): string;
}

const twoDigits = (n: number) => String(n).padStart(2, '0');
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

// "EUR 12.50"
class PointMoney implements MoneyFormat {
  format(cents: number): string {
    return `EUR ${Math.floor(cents / 100)}.${twoDigits(cents % 100)}`;
  }
}
// "12,50 EUR"
class CommaMoney implements MoneyFormat {
  format(cents: number): string {
    return `${Math.floor(cents / 100)},${twoDigits(cents % 100)} EUR`;
  }
}
// "17 Oct 2026"
class NamedMonthDates implements DateFormat {
  format(date: TravelDate): string {
    return `${date.day} ${months[date.month - 1]} ${date.year}`;
  }
}
// "17.10.2026"
class DottedDates implements DateFormat {
  format(date: TravelDate): string {
    return `${twoDigits(date.day)}.${twoDigits(date.month)}.${date.year}`;
  }
}

// Two callers, each choosing its formats itself.
function confirmationLine(booking: Booking): string {
  const money = booking.locale === 'de' ? new CommaMoney() : new PointMoney();
  const dates = booking.locale === 'de' ? new DottedDates() : new NamedMonthDates();
  const price = money.format(booking.cents);
  return `${booking.id} confirmed: ${price}, travelling ${dates.format(booking.date)}`;
}
function reminderLine(booking: Booking): string {
  const money = booking.locale === 'de' ? new CommaMoney() : new PointMoney();
  const dates = new NamedMonthDates();
  const price = money.format(booking.cents);
  return `${booking.id} reminder: pay ${price} by ${dates.format(booking.date)}`;
}