2012-09-05 13 views
8

Jestem całkiem nowa w Scali i piszę bibliotekę API i próbuję używać tylko vals i niezmiennych obiektów (jeśli to możliwe). Próbuję wybrać właściwą metodę modelowania obiektów. Załóżmy, że istnieje kilka obiektów „post”, które wszystkie mają pewne wspólne pola, a każdy dodaje swoje własne specyficzne pola:Scala Extended Sparametryzowana Klasa Abstrakcyjna

abstract class Post(val id: Int, val name: String, ...) 
case class TextPost(val id: Int, val name: String, ..., val text: String) extends Post(id, name, ...) 
case class PhotoPost(val id: Int, val name: String, ..., val url: String) extends Post(id, name, ...) 

Metoda ta dodaje wiele powtarzających się zgłoszeń i kodu boiler-talerz, szczególnie gdy chodzi o wiele Podklasy pocztowe i wspólne pola deklarowane są w klasie abstrakcyjnej. Sposób, w jaki to widzę, jest spowodowany użyciem valsów, które mogą być inicjowane tylko przez konstruktora. Czy istnieje lepszy sposób na tworzenie relacji tutaj opisanych, czy też muszę cierpieć z tego powodu, że chcę używać tylko niezmiennych obiektów?

Odpowiedz

8

Po pierwsze, modyfikatory val w konstruktorach klasy case są nadmiarowe, kompilator Scali da je już "za darmo" (jest to jedna z cech klas przypadków). Jeśli rzeczywiście próbował skompilować, że widać, że błąd występuje, ponieważ nadrzędnym nierejestrowanej:

abstract class Post(val id: Int, val name: String) 
case class TextPost(id: Int, name: String) extends Post(id, name) 
case class PhotoPost(id: Int, name: String) extends Post(id, name) 

<console>:10: error: overriding value id in class Post of type Int; 
value id needs `override' modifier 
      case class PhotoPost(id: Int, name: String) extends Post(id, name) 
           ^

ja osobiście polecam użyć klasy abstrakcyjne z argumentów konstruktora w jak najmniejszym stopniu. Scala ma cechy, które wykonują lepszą pracę tutaj. Zamiast argumentów konstruktora deklarujesz (abstrakcyjnie) pola; następnie zajęcia przypadków kopać i ze względu na ich automatycznie czyni swoje argumenty vals, skończysz:

trait Post { def id: Int; def name: String } 
case class TextPost(id: Int, name: String, text: String) extends Post 
case class PhotoPost(id: Int, name: String, url: String) extends Post 

Ponadto, jeśli dodać sealed do trait Post, można bezpiecznie korzystać z podtypów Post w dopasowaniu wzoru bez przypadkowo brakujące przypadki.

+0

Dzięki za odpowiedź! Jeśli chodzi o kod płyty kotła, metoda ta w zasadzie nie pozwala mi zadeklarować każdego parametru w klasach pochodnych jako wartości, ale nadal muszę powtarzać wszystkie parametry w każdej deklaracji, prawda? Podkreślam to, ponieważ mam 8 podklas i 15 współdzielonych pól, a to wydaje się być tak ogromnym przypadkiem DRY. Ale rozumiem z twojej odpowiedzi, że nie da się tego obejść, prawda? – orrsella

+1

Jedyną inną alternatywą jest _nie_ wdrożenie wspólnego interfejsu ('Post'). Oznacza to, że musisz _preferować_ typ, aby go zawrzeć, ale nie wiem, czy tego właśnie chcesz: 'case class Post (id: Int, name: String); klasa przypadku TextPost (post: Post, text: String) '. –

+5

Nie uważam, że ta odpowiedź jest wystarczająca, ponieważ nie odpowiada na pytanie o płytę kotła ... –

Powiązane problemy