15 czerwca 2013

Męk intelektualnych kontynuacja - taskassin na GitHubie dzień 2

Wczorajszych, piątkowych męk intelektualnych związanych z tworzeniem aplikacji taskassin w Scali nie mniej i dzisiaj, w sobotę. Mimo braku czasu na specjalnie długie zastanawianie się, co i jak należałoby zrobić, aby poprawić moją sytuację, usprawniłem TaskListTest, w którym próbuję się odnaleźć i przekuwam idee w kod.

Dzisiaj postanowiłem zanurzyć się w dokumentacji ScalaTest, aby nie skończyć z przekopiowaniem części kodu testu. W ten sposób znalazłem metodę pending, która (jak możnaby wywnioskować z jej nazwy) w ciele metody testującej pozwala na opóźnienie momentu, w którym test faktycznie powstanie. Innymi słowy, wiesz, że taki test musi powstać, ale nie bardzo wiesz, jak należałoby go napisać (aka zaimplementować). Sprytne.

W ten sposób powstał test jak poniżej.
describe("List of three tasks (with 5 minutes in between)") {
  it("should return the tasks for today") (pending)
}
Wykonanie takiego testu przechodzi poprawnie, ale oznaczone jest żółtym kolorem.

To lubię!

Skorzystałem również z potrójnej równości, która przy niepomyślnym porównaniu wyświetla różnice w bardziej zrozumiałej postaci, z dokładną informacją o różnicy.

Ostatnią zmianą było użycie before z traitu org.scalatest.BeforeAndAfter.
before {
  val t2 = new Task("t2", DateTime.now + 2.minutes)

  /* http://mkaz.com/solog/how-to-sort-collections-in-scala */
  /* sort by due date */
  def compByDueDate(t1: Task, t2: Task) = t1.due < t2.due

  ts = List(t2, t1) sortWith compByDueDate
}
Krótka lektura Getting started with FunSpec, która zajęła mi bagatela 10 minut, a wiedzy i zmian ogrom (na długie godziny). Niestety kodu produkcyjnego wciąż mało. Zmiany są dostępne w projekcie taskassin na GitHubie.

5 komentarzy:

  1. Sam również planuję coś skrobnąć w Scali, pewno Twoje doświadczenia przydadzą się i mi ;) Powodzenia i wytrwałości!

    OdpowiedzUsuń
    Odpowiedzi
    1. Na to liczę! Co mnie zwykle przytłacza, to mnogość projektów w danym języku bez opisu, dlaczego podejmowane były te a nie inne techniki i rozwiązania. Koniecznie opisuj swoje doświadczenia na blogu.

      Usuń
  2. Cześć, zacząłem się przyglądać Twoim przygodom z Taskassin i mam kilka przemyśleń, które mogą dać inną perspektywę na pewne kwestie.

    Na początek sortowanie listy zadań. Oprócz twojego podejścia z podaniem nazwanej funkcji w metodzie sortWith można zastosować trzy inne podejścia (pewnie może być ich więcej, ale te są chyba najprostsze):

    1. Jeżeli sortowanie zadań będzie zawsze odbywać się po polu due, to wykorzystajmy obiektowość i rozszerzmy Task o właściwość Ordered[Task]:

    case class Task(label: String, due: DateTime, timeUnits: Int = 6,
    recurring: Boolean = false, recurTimes: Int = 0, intervalUnits: Int = 0) extends Ordered[Task] {
    def compare(other: Task): Int = this.due.compareTo(other.due)
    }

    dzięki temu sortowanie listy zadań będzie można wykonać następująco:

    val ts = List(t1, t2, t3).sorted

    2. Jeżeli przyjmiemy, że zadania można sortować na kilka sposobów, np. dodatkowo po nazwie, to w klasie towarzyszącej możemy zdefiniować konkretne sposoby porządkowania wykorzystując Ordering[task]

    case class Task(label: String, due: DateTime, timeUnits: Int = 6,
    recurring: Boolean = false, recurTimes: Int = 0, intervalUnits: Int = 0)

    object Task {
    val orderingByDue = Ordering.fromLessThan((a:Task, b:Task) => a.due < b.due)
    val orderingByLabel = Ordering.fromLessThan[Task](_.label < _.label)
    }

    (orderingByLabel wykorzystuje skróconą notację funkcji anonimowej)

    W takiej stytuacji sortowanie wygląda następująco:

    val ts = List(t1, t2, t3).sorted(Task.orderingByDue)

    3. Jeżeli jednak nie chcemy narzucać programiście jakiego sortowania powinien używać można wykorzystać metodę sortBy, której parametr wskazuje, którego pola klasy należy użyć do sortowania:

    val ts = List(t1, t2, t3).sortBy((t:Task) => t.due)

    A jeszcze lepiej użyć skróconej i bardziej czytelnej formy:

    val ts = List(t1, t2, t3).sortBy(_.due)

    4. Jeżeli rozwiązanie z punktu 3. nie jest wystarczające (bo. np sortowanie jest bardziej skomplikowane) metoda sortBy, która przyjmuje funkcję według której zostaną posortowane elementy, będzie jak znalazł:

    Ponieważ Ty ją wykorzystałeś pokażę tylko przykład formy skróconej:

    val ts = List(t1, t2, t3).sortWith(_.due < _.due)


    Podsumowując, ja skłaniałbym się ku metodom 1. lub 2. ponieważ umożliwiają zdefiniowanie kontekstu biznesowego, którego programista nie będzie musiał odkrywać za każdym razem, gdy będzie chciał posortować kolekcję (oczywiście mowa o bardzie skomplikowanych przypadkach niż ten w taskassin). I oczywiście na pewno jest jeszcze dużo więcej sposobów sortowania, ale chciałem dodać swoje 5 groszy ;)

    OdpowiedzUsuń