2012-02-20 17 views
9

Bawiłem się z pojęciem wyliczenia/stałych w coffeescript (http://coffeescript.org/) i wymyśliłem poniższy kod, który wydaje się być OK. Jak mogę to ulepszyć, aby było jeszcze lepiej dla czegoś, do czego dopasuje się enum? Wyszukiwania google nie wykazały jeszcze satysfakcji.coffeescript i wartości wyliczeniowe

class SomeService 

    @SomeEnumValue : 400 
    @SomeOtherValue : 402 

    someFunc: -> 
    SomeService.SomeEnumValue 

ok = new SomeService() 
alert ok.someFunc() 

if (ok.someFunc() == SomeService.SomeEnumValue) then alert ' some enum value' 
+2

Czego dokładnie szukasz? JavaScript nie ma stałych, a jego wyliczenia są po prostu hashami, więc wszystko, co pojawi się w CoffeeScript, będzie miało podobny kształt. Dla wyliczenia CS, po prostu buduję skrót: @Colors = Red: 1, Blue: 2 – egoodberry

+0

Chciałem się upewnić, że czegoś mi nie brakuje. Dzięki! – finneycanhelp

Odpowiedz

19

Cała koncepcja wyliczenia jest po prostu bezużyteczny w dynamicznych języków jak jest krotka, wpisane listy, mapy i wiele innych rzeczy, i Javascript (coffeescript) jest dynamiczna. Pracując z dynamicznym językiem, musisz po prostu zapomnieć o sprawdzaniu typu i użyć istniejących, bardziej ogólnych konstrukcji, aby rozwiązać problem. Użyj tablic zamiast list i krotek, używaj obiektów zamiast map i wyliczeń i po prostu zaufaj typowi wartości przekazanej do funkcji, ale mocno przetestuj swój kod. Na lepsze lub na gorsze (dla gorszych IMO), tak właśnie dzieje się tutaj praca.

W twoim przypadku Polecam tylko przechowywanie wartości w jednoelementowy obiektu, tak jak poniżej:

HTTPStatusCodes = 
    ok : 200 
    badRequest : 400 
    unauthorized : 401 

i dostępu do niego tak:

class SomeService 
    okCode: -> 
    HTTPStatusCodes.ok 
    failureCodes: -> 
    code for key, code of HTTPStatusCodes when code >= 400 
+3

W jaki sposób krotki i mapy są bezużyteczne? – Darthfett

+6

@Darthfett Nie usuwaj moich słów z kontekstu. Powiedziałem, że są bezużyteczne ** w językach dynamicznych **. A to dlatego, że nie ma ograniczeń typu w językach dynamicznych, które na przykład kasują granicę między krotką a tablicą. Z kolei w językach statycznych takie konstrukcje są nieodzowne. –

+4

Uwielbiam twoje rozwiązanie i jest dla mnie idealne. Rozumiem, że emumy są bezużyteczne w językach dynamicznych pod względem ograniczeń typu, ale pod względem czytelności kodu są absolutnie niezbędne. Twoje rozwiązanie zapewnia to doskonale. –

4

nie zgadzam się ze stwierdzeniem, że wyliczenia są bezużyteczne ze względu na dynamiczną naturę Javascriptu lub Enums to mniej gloryfikowane hasze.

Cytując Wikipedię: "Zmiennej, która została zadeklarowana jako posiadająca typ wyliczeniowy, można przypisać dowolny moduł wyliczający jako wartość." I tylko te enumeratory są możliwe jako wartości.

Coffeescript może z łatwością i syntaktycznie przyjemnie emulować Enum. W tym obsługa błędów na niepoprawnych wartościach wyliczeniowych (ale tylko w czasie wykonywania)

Utworzono przykład, który ma głównie charakter funkcjonalny i wykorzystuje anonimowe funkcje oddzwaniania jako podstawienie - w zasadzie zastępuje operatora przypisania "=" dla Operator funkcji Coffeescripts "->". Tworzy najbardziej składnie zwarty kod w mojej książce. Jednak bardziej klasowe podejście jest z pewnością możliwe.

#define enumeration 
httpcodes = Enum 
    ok: 200 
    badRequest: 400 
    unauthorized: 401 
    server_error: 500 

#set enum variables with some default state 
chrome = httpcodes -> @server_error 
firefox = httpcodes -> @server_error 
safari = httpcodes -> @server_error 

console.log "httpcodes.ok:" + httpcodes.ok 

#change enum value 
chrome -> @ok 
firefox -> @badRequest 
safari -> @unauthorized 

console.log "chrome:" + chrome -> 
console.log "firefox:" + firefox -> 
console.log "safari:" + safari -> 

console.log "(chrome ->) == httpcodes.ok:" + ((chrome ->) == httpcodes.ok) 

#set an invalid value 
try 
    safari -> 999 
catch err 
    console.log err 
    console.log "safari:" + safari -> 

A oto kod, aby utworzyć Enum (trzeba umieścić to na górze kod jeśli chcesz go uruchomić. Po prostu chciał pokazać kod użytkowania przed kodem realizacji

Enum = (enumeration)-> 
    check = (value)-> 
     newval = null 
     for key, val of enumeration 
      if val == value 
       newval = value 
       break 
     if !newval 
      throw "Invalid Enum Value: #{value}" 

    result = (init)-> 
     state = init.call(enumeration) 
     check state 
     (callback)-> 
       value = callback.call(enumeration) 
       if value == null or value == undefined 
        return state 
       else 
        check value 
        state = value 

     for key, value of enumeration 
      result[key] = value 

     result 

Oczywiście byłoby znacznie ładniej, gdyby Coffeescript zawierał makra syntaktyczne. Więc moglibyśmy napisać

Enum httpcodes 
    ok: 200 
    badrequest: 400 

i

chrome = httpcodes 'ok 
#or 
chrome := 'ok 
+8

Przykro mi, ale to tylko bałagan. 1. To nie jest enum, który robisz, ale obiekt o stanie przełączalnym i nie ma nic wspólnego z twoim cytatem z Wikipedii. 2. Zarówno obiekt, jak i zmienne, do których go przypisaliśmy, są nadal niezabezpieczone przed przypisaniem do wartości dowolnego innego typu, ponieważ po raz kolejny JavaScript jest dynamiczny. 3. Cała ta fasada nieuchronnie obniży wydajność. 4. Kod mówi sam za siebie o tym, jak skomplikowany i zarządzalny jest. A więc wszystkie te straty za to, co dokładnie przynosi korzyść? Przykro mi, ale w zasadzie to tylko nadmierna komplikacja bez żadnego praktycznego celu. –

+1

na 1) czym jest wyliczenie inne niż obiekt z predefiniowanymi przełączalnymi "stanami"? na 2) byłoby bardzo łatwo rozszerzyć to, aby utworzyć właściwość na dowolnym hoście poprzez 'Object.defineProperty', której nie można nadpisać, na 3) tak będzie wolniej. Ale jeśli nie możesz pozwolić sobie na jedną lub dwie pośrednie przez wywołania funkcji - prawdopodobnie nie powinieneś używać środowiska javascript.na 4) Uważam, że 30 LOC jest łatwe do zarządzania. Złożoność wynika z podejścia "funkcjonalnego", a podejście to jest ponownie wynikiem największego zagęszczenia. Ale tak czy inaczej - YMMY _ nie podoba ci się to? nie używaj tego. – robkuz

+2

1. Enum jest typem, który ma ograniczony zestaw możliwych wartości. To nie jest obiekt, jego wartości są jego przedmiotami. 3. Traktowanie wydajności jako "nieistotnej" w jakimkolwiek środowisku może jedynie mówić o kwalifikacjach programisty. I nie chodzi tylko o pośrednie tylko w twoim przypadku, ale o niepotrzebne przechodzenie. 4. Och, nie, nie nazwałeś po prostu funkcjonalnością opartą na stanie funkcjonalnym opartym na stanie). Nie odwołuj się do rzeczy, o których oczywiście nic nie wiesz, tylko ze względu na argument. –

5

Wiem, że późno do partii, ale dla potomności Oferuję do góry „coffeethonic” rozwiązanie (w duchu mniej pisania) :

[ a, b, c ] = [1..3] 
2
Colors = Object.freeze({ 
    RED: 'red' 
    GREEN: 'green' 
    BLUE: 'blue' 
}) 
console.log Colors.RED 
# red 

wartości są stałe (nie można ich zmieniać):

Colors.RED = 'black' 
console.log Colors.RED 
# red 
1

Zacząłem dzień zastanawiając się nad enums w coffeescript i zakończyłem go rozwiązaniem I published on github (available in npm, bower, meteor too). Próbowałem opracować podobne do java wyliczenia, ale jeszcze bardziej elastyczne, biorąc pod uwagę połączenie między dziedziczeniem prototypów i klasycznym dziedziczeniem coffeescript.

Oto jak to dopasować swój kod:

class SomeService 
    someFunc: -> SomeService.SomeEnumValue 
    #A cool hack, but it must be the last class statement. 
    #Your class will now inherit this enumeration's properties. 
    #If you find this too hacky, you can always have a public static 
    #states class property instead. 
    @__proto__:new Enumeration('SomeService',{ 
      SomeEnumValue :400 
      SomeOtherValue:402 
     }) 

ok = new SomeService() 

alert ok.someFunc().id() #shows 400 

if (ok.someFunc() is SomeService.SomeEnumValue) then alert ' some enum value' 

Ale co jest fajne w tej realizacji jest to, że wyliczenia mogą mieć określone dziedziny, i dziedziczą z prototypu (3d konstruktora argumentu), choć gwarantuje niepowtarzalność . To pozwala na refaktorowanie kodu i przenoszenie pewnej logiki do tej funkcji. Zapytajmy teraz o tę wartość, aby powiedzieć nam coś, kiedy trzeba, definiując funkcję tell.

class SomeService 
    someFunc: -> SomeService.SomeEnumValue 
    #A cool hack, but it must be the last class statement. 
    #Your class will now inherit this enumeration's properties. 
    #If you find this too hacky, you can always have a public static 
    #states class property instead. 
    @__proto__:new Enumeration('SomeService', 
       SomeEnumValue : { _id:400, text: ' some enum value' } 
       SomeOtherValue: { _id:402, text: null } 
     , tell:->if @text? then alert @text) 

ok = new SomeService() 

alert ok.someFunc().id() #shows 400 

ok.someFunc().tell() 

Nadzieja to pomaga komuś, można sprawdzić adres GitHub aby przyjrzeć się realizacji, a niektóre bardziej szczegółową dokumentację napisałem.

Powiązane problemy