Nie znam podstaw - uświadamiam sobie...

1
somekind napisał(a):
myśliwy napisał(a):

Ja pamiętam moją rekrutację na programistę C#. W zadaniu rzuciłem wyjątek w konstruktorze i wielkie halo, że tak się nie robi. No to ja tłumaczę, że jest odśmiecacz pamięci oraz try-catch. Nie zrozumieli tego i zostałem odrzucony.

Odśmiecacz i try-catch nie mają nic do rzeczy. Konstruktor musi się wykonać szybko i niezawodnie, jak int x = 7, dlatego nie rzuca się w nim wyjątków, nie przeprowadza operacji I/O, nie deserializuje, nie generuje miliona losowych liczb, itd. Jeśli takie operacje są potrzebne do utworzenia obiektu, to pisze się statyczną metodę fabrykującą w danej klasie.

Uwielbiam takie prawdy absolutne, akjomaty, dogmaty, pojecia pierwotne, itp. itd. bla bla.
Generalnie zgadza sie ze konstruktor powinien byc szybki, ogolnie staram sie zeby zawieral tylko przypisywanie parametrow do pol (DI). Nie zgadzam sie jednak, ze zawsze tak musi byc i zawsze tak jest.

0

Alez oczywiscie, ze nie trzeba. Tak samo nie trzeba tworzyc malych klas i metod. Nie trzeba rowniez dekomponowac systemu.

Jesli ktos robi cos takiego w konstruktorze:

if(something(asdasd)) {
  throw new ArgumentException("asdasd");
}

To:
#Zeby stworzyc obiekt wszystkie parametry bezapelacyjnie musza byc poprawne. Co niekonircznie zawsze jest porzadane.
#Zeby stworzyc obiekt tej klasy i oczywiscie wszystkich innych pochodnych musze za kazdym razem pisac try..catch. Wyobraz to sobie, gdy uzywasz DI jaka masz kontrole nad obsluga wyjatkow... :|
#Jesli alokuje tam jakies zasoby, to cuz... peszek. Bo wpadlem na genialny pomysl wyrzucenia wyjatkow w konstruktorze, to mi pewno memleaki nie przeszkadzaja w zaden sposob.

Nie ma takich sytuacji gdzie prosty konstruktor moze rzucic wyjatek i to jeszcze ma sens. Jak juz pisalem walidacja argumentow to jest sytuacja spodziewana, a nie wyjatkowa, widac roznice?

Ale sure... mozna mowic "Przeciez to poprawne, w tutorialu tak pisali".

0
emfałsi napisał(a):

http://msdn.microsoft.com/en-us/library/ms229060(v=vs.110).aspx

DO throw exceptions from instance constructors, if appropriate.

:)

0
n0name_l napisał(a):

walidacja argumentow to jest sytuacja spodziewana, a nie wyjatkowa, widac roznice?

Jak przekazywany argument jest null lub ma nieprawidłową wartość to jest to sytuacja spodziewana? Jest to sytuacja wyjątkowa, bo program przez tą wartość nie wykona to czego się user spodziewa.

1

Ok.

class BooksController {
  private readonly IBookService _bookService;
  private readonly IAuthorizationService _authorizationService;

  public BooksController(IBookService bookService, IAuthorizationService authService) {
    _bookService = bookService;
    _authorizationService = authService;
  }

  public ActionResult Show(string title) {
    var book = _bookService.FindBookByTitle(title);
    if(book == null)
      return HttpNotFound();
    return View(book);
  }
}

Test tej metody wymaga 1 mocka. W twoim przypadku, test wymaga 2 mockow. Po co mialbym sobie zycie utrudniac?

0
myśliwy napisał(a):
n0name_l napisał(a):

walidacja argumentow to jest sytuacja spodziewana, a nie wyjatkowa, widac roznice?

Jak przekazywany argument jest null lub ma nieprawidłową wartość to jest to sytuacja spodziewana? Jest to sytuacja wyjątkowa, bo program przez tą wartość nie wykona to czego się user spodziewa.

Wszystko zależy od kontekstu. Jeśli twój konstruktor tworzy obiekt i ma dostać inta z danego zakresu, to spodziewasz się, że ktoś może przekazać inta z poza zakresu i powinieneś to obsłużyć przy przekazywaniu argumentu. Ogólnie wszystkie możliwe niepoprawne dane wejściowe, które umiesz zidentyfikować na poziomie przekazywania danych - powinieneś obsługiwać tam. Wyjątki w konstruktorze mogą być rzucane, jeśli faktycznie jest tego dobre uzsadanienie - że ktoś wrzuci nulla do twojej funkcyjki - zwykle możesz przewidzieć.

Poza tym używajcie wzorców konstrukcyjnych, to wam roboty oszczędzi nad rozkminami tego typu.

0

Wydaje mi się, że cała ta zasada o nierzucaniu wyjątków w konstruktorze wzięła się z przyzwyczajeń z C++, gdzie dość łatwo strzelić sobie w stopę np. nie używając list inicjalizacyjnych konstruktora, tylko wykonując przypisania na dodatek alokujące pamięć. W efekcie łatwo o wyciek i niektórzy tego zabraniają (np. Google).

Natomiast w językach takich jak Java i C# problemu nie ma. Zresztą każdy konstruktor może rzucić wyjątek, choćby OutOfMemoryError. Jedyne o czym trzeba pamiętać przy rzucaniu wyjątków w konstruktorze, to żeby zwolnić wszelkie zasoby w bloku finally (dotyczy to każdej metody, nie tylko konstruktora).

Przez takie głupie dogmatyczne unikanie realnej pracy i wyjątków w konstruktorach później widzi się taki kod, gdzie właściwa inicjalizacja jest przesunięta do metody "init".

1
mućka napisał(a):

Uwielbiam takie prawdy absolutne, akjomaty, dogmaty, pojecia pierwotne, itp. itd. bla bla.
Generalnie zgadza sie ze konstruktor powinien byc szybki, ogolnie staram sie zeby zawieral tylko przypisywanie parametrow do pol (DI). Nie zgadzam sie jednak, ze zawsze tak musi byc i zawsze tak jest.

A czytasz z bazy albo z pliku w konstruktorze? Bo tego głównie te moje "dogmaty" dotyczą.
To, co napisałem jest banalne do zapewnienia, a pozwala uniknąć potencjalnych dziwnych zachowań.

A co do samych wyjątków w konstruktorze - nie przypominam sobie, żebym kiedykolwiek miał potrzebę taki rzucić. Jakiś realny przykład?

0

Kiedy rzucam wyjątek w konstruktorze:

  • gdy mam z gruntu niepoprawne parametry w rodzaju null (wtedy należy rzucać wyjątek ASAP)

Co robię gdy mam wielopoziomowe przetwarzanie zrobić w konstruktorze? (np. wykonanie operacji bazodanowej)

  • przerzucam funkcjonalność do funkcji init - @PostConstruct, fabryki lub do funkcji leniwie inicjalizującej obiekt

C++
http://www.parashift.com/c++-faq-lite/ctors-can-throw.html
http://en.wikibooks.org/wiki/C%2B%2B_Programming/Exception_Handling#Constructors_and_destructors

Java
http://www.javaspecialists.eu/archive/Issue120.html

0

Oczywiście, że należy rzucać wyjątki w konstruktorze, gdy parametry nie spełniają określonych założeń.

Np konstruktor klasy URL rzuca MalformedURLException
http://docs.oracle.com/javase/7/docs/api/java/net/URL.html#URL%28java.lang.String%29

Wyjątkiem są klasy z konstruktorami private/protected tworzonymi przez fabryki. Sprawdzenie powinno być w fabryce.

1 użytkowników online, w tym zalogowanych: 0, gości: 1