Placing items of different types in a list with being able to map on them

100 views Asked by At

So I made a post earlier about accessing a field of a subclass from within the superclass to solve a problem i have. But they made it clear that it is practically impossible. So I made a little example of what i want to achieve:

abstract class LotteryTicket(val numbers: String) { 

val price: Int

}

//The numbers are different for each ticket and are used to denote a winner in the end.

class GoldTicket(numbers: String) extends LotteryTicket(person) {

val price: Int = 10

}

class SilverTicket(numbers: String) extends LotteryTicket(person) {

val price: Int = 5

}

abstract class Drink {

val price: Int

}

object Water extends Drink {

val price: Int = 1

}

object Coffee extends Drink {

val price: Int = 2

}

class Bill

class Customer

The class 'Bill' should contain a list which can include Drinks as well as LotteryTickets, for which the total can be calculated and the Customer has to be able to make such a bill. The Customer class also needs a method which confirms the purchase and checks if the numbers on his LottoryTicket are different for every Ticket he bought. Because when he has the same number on 2 tickets The confirmation fails. It alse must be possible to add new products in the future (like food for example) in an easy way (without changing the core code).

2

There are 2 answers

3
Dima On BEST ANSWER

You want your "billable" items implement a trait, that exposes their common features.

  trait Billable { 
      def price: Int
  }
  class LotteryTicket(val numbers: String, val price: Int) extends Billable
  class GoldTicket(n: String) extends LotteryTicket(n, 10)
  class SilverTockent(n: String) extends LotteryTicket(n, 5)

  class Drink(val price: Int) extends Billable
  object Water extends Drink(1)
  object Coffee extends Drink(2)

  case class Bill(val items: Seq[Billable]= Seq.empty)
  {
    def amount = items.map(_.price).sum
    def add(b: Billable) = copy(b +: items)
  }

  case class Customer(bill: Bill = Bill()) {
    def buy(ticket: LotteryTicket) = {
      // validate numbers, whatever
      Customer(bill.add(ticket))
    }

    def buy(drink: Drink) = {
      Customer(bill.add(drink)
    }

    def howMuch = bill.total

    def validateAllTickets = bill.items.foreach {
       case ticket: LotteryTicket => makeSureNumberIsGood(ticket.numbers)
       case _ =>
    }
  }
1
f7h On

A solution for the Bill class using the Dima's classes :

class BillImplementation {
  private var container = Seq[Billable]()

  def addProduct(product: Billable) = container :+= product // this add the product element to the container Seq

  def listOfAllBillableInThis = container

  def totalSum = container.map(_.price).sum

  def isThere2ticketsWithSameNumbers: Boolean = {
    var containerOfTickets = Seq[LotteryTicket]()
    for (p <- container) {
      p match {
        case lo: LotteryTicket => containerOfTickets = containerOfTickets :+ lo
        case _ =>
      }
    }
    val numbersMap = containerOfTickets.map(_.n)
    numbersMap.distinct.size != numbersMap.size
  }

  def empty: Unit = container = Nil
}

And do not use Int for prices but BigDecimal.