Najgorsze anty-wzory, które napotkałeś [zamknięte]

9

Jakie są najgorsze anty-wzorce, na które natrafiłeś w swojej karierze programisty?

Jestem głównie zaangażowany w Javę, chociaż prawdopodobnie jest ona niezależna od języka.

Myślę, że najgorsze jest to, co nazywam głównym anty-wzorem . Oznacza program składający się z pojedynczej, bardzo dużej klasy (czasem wraz z parą małych klas), która zawiera całą logikę. Zwykle z dużą pętlą, w której zawarta jest cała logika biznesowa, czasami zawierająca dziesiątki tysięcy wierszy kodu.

стривігор
źródło

Odpowiedzi:

38

Skomentowano kod. Bloki tego, może setki linii. Teoria jest taka, hej, skomentowano ją, nie wyrządza żadnej szkody i być może będziemy jej potrzebować w przyszłości.

Navi
źródło
7
Przynajmniej możesz go bezpiecznie usunąć. W przeciwieństwie do kogoś, z kim pracowałem, często zmieniał nazwy procedur i pozostawiał martwy kod dostępny. Blech.
Jay
@Cyrena, pracowałaś też z Ericiem?!?
CaffGeek
Kiedyś miałem sporo kodu, który używał #ifdef COMMENT do blokowania rzeczy. Działało świetnie, dopóki ktoś nie zdefiniował KOMENTARZA.
Michael Kohne
9
To kolejny przypadek, w którym opłaca się kontrola wersji. Możesz usunąć kod bez pytania, czy może być potrzebny w przyszłości, ponieważ można go odzyskać za pomocą kilku naciśnięć klawiszy.
Jason B
5
Myślę, że skomentowany kod ma miejsce, jeśli poprzedzisz go komentarzem wyjaśniającym, dlaczego tak jest. Czasami zostawiam skomentowany blok, który reprezentuje potencjalną drogę, o której myślałem, i zostawiam z komentarzem, dlaczego tego nie zrobiłem. @Jason, zdecydowanie słyszę o tym, że jest to rola kontroli wersji, ale usunięty kod w kontroli wersji nie jest bardzo wykrywalny.
nlawalker
19

Uderzę inny oczywisty „kopiowaniem makaronu”. Kopiowanie kodu, który jest prawie identyczny z tym, czego chcesz, a następnie zmiana kilku rzeczy (zamiast wypakowywania do metody).

Jest to szczególnie rozpowszechnione w niektórych testowych kodach funkcjonalnych i API z późnych lat 90-tych: dosłownie setki (lub nawet tysiące) prawie identycznych przypadków testowych, które można podzielić na kilka funkcji, które przyjmują 3 lub 4 parametry - lub nawet lepiej , coś opartego na danych. Moją pierwszą pracą po studiach było dosłownie 8 miesięcy przepisywania i refaktoryzacji tysięcy wierszy makaronu, które używały przestarzałych metod. Kiedy skończyłem, pliki testowe miały mniej niż 10 oryginalnego rozmiaru i były o wiele łatwiejsze w utrzymaniu (i czytelności!).

Ethel Evans
źródło
16

Myślę, że mogę dużo napisać o Pattern Mania i rozwiązaniach, które można rozwiązać przy mniejszym wysiłku, ale wolałbym wskazać świetny artykuł, który przeczytałem ostatnio, z fantastycznym przykładem tego, jak proste rozwiązanie może być nadmiernie skomplikowane .

Jak (nie) pisać w Factorial w Javie, tak więc umieściliśmy fabrykę w twoim algorytmie

Andrey Taptunov
źródło
3
Niesamowite! Gdzie jest teraz usługa FactorialWebService? : P
FrustratedWithFormsDesigner
1
Nazywam to Pattern Fever ... wszyscy dostają to w pewnym momencie swojej kariery. Im lepiej wśród nas, wykraczamy poza to. Miałem wywiad, w którym facet zapytał mnie, kiedy powinienem pomyśleć o wzorach. Powiedziałem mu, że pozwalam im ewoluować w system. Powiedział „Nie, powinieneś używać wzorów od samego początku”.
Michael Brown
FactoryFactories to tylko symptom chęci odroczenia rzeczywistego wyboru tak bardzo, jak to możliwe, ale nadal kończy się albo jego zakodowaniem na stałe, albo zamapowaniem zewnętrznej wartości ciągu na fragment kodu. Dependency Injection jest lepszym rozwiązaniem.
Wzory są artefaktami dobrego projektu - powinny być traktowane jako takie. Lubię je opisywać raczej jako definicje niż rozwiązania. Są przydatne podczas komunikacji, ale niekoniecznie w projektowaniu.
Michael K
Dzięki za dobrą lekturę.
Syg
14

Złoty Młot

„Mam młotek, a wszystko inne to gwóźdź”.

Maglob
źródło
14

Regiony

W języku C # możesz zdefiniować region kodu, który można zwinąć w IDE, ukrywając go, chyba że chcesz poradzić sobie z tym kodem. Byłem na (obecnie jestem) projekcie, w którym regiony rozciągały się na setki linii (szkoda, że ​​nie przesadzam) i było wiele regionów w funkcji tysiąca linii (znowu chciałbym żartować).

Z drugiej strony programista, który stworzył region, wykonał bardzo dobrą robotę, identyfikując określone funkcje w regionie. Do tego stopnia, że ​​mogłem zrobić metodę wyodrębnienia dla regionu i przejść dalej.

Regiony zachęcają deweloperów do „ukrywania” swoich bzdur na widoku.

Michael Brown
źródło
15
+1 dla regionów zachęca programistów do „ukrywania” swoich badziewi na widoku. Jednak przy prawidłowym użyciu stwierdzam, że regiony pomagają logicznie pogrupować kod i łatwo go znaleźć w późniejszym czasie.
Darren Young,
Od dawna jest to problem ze składanymi edytorami. Robiłem to samo, kiedy ostatnio programowałem w OCCAM.
Michael Kohne,
2
Uwielbiam regiony ... ale tylko dla organizacji ... nie ukrywając cyfrowych ryz kodu.
IAbstract
3
+1. Dlatego też używanie regionów narusza regułę StyleCop SA1124 DoNotUseRegions.
Arseni Mourzenko
1
Mam projekty, które działają z ogromną liczbą pól w SQL, a kod, który aktualizuje / tworzy to wiele wierszy. Region #region SQL Updatesprawia , że jest zwijany, więc wymagane jest mniej przewijania.
JYelton,
12

Dla mnie najgorszym wzorem jest Kopiuj / Wklej .

LennyProgrammers
źródło
28
Dla mnie najgorszym wzorem jest Kopiuj / Wklej.
Andrew Arnold
9

Nie przeciążające metody:

// Do something
public int doSomething(Data d);

// Do same thing, but with some other data
public int doSomething2(SomeOtherData d);

Wyraźnie pokazuje, że programista nie zrozumiał przeciążenia.

Michael K.
źródło
5

Sekwencje przełączania pętli

http://en.wikipedia.org/wiki/Loop-switch_sequence

Drażnią mnie bez końca i naprawdę pokazują, jak niedoświadczony był deweloper

CaffGeek
źródło
Ach, maszyna stanu kończąca :)
Dlaczego musisz przywoływać te zakopane wspomnienia? Miałem taki miły dzień.
Malachi
5

Miękkie kodowanie , tj. Gdy programiści robią wszystko, aby uniknąć kodowania na twardo i kończą po drugiej stronie skali - zależności „na stałe”.

AareP
źródło
4

Zachowaj stan w kliencie.

Kiedyś pracował z aplikacją internetową, w której cały stan był przechowywany w przeglądarce. (Aby zapewnić bezproblemową skalowalność, wszystkie wyjątki zostały zignorowane).

użytkownik1249
źródło
Czy możesz prosić o opracowanie? Imho, w przypadku aplikacji internetowej, jest w porządku, gdy jedynym stanem jest przeglądarka (tzn. Pliki cookie), a serwer jest „współdzielony”. Czy masz na myśli „stan” jak w „bazie danych aplikacji”?
keppla
Stan: jak w rzeczywistych danych do działania.
O, masz moje najgłębsze współczucie.
keppla
4

Masowe meldowanie

Nienawidzę, gdy widzę, że programista nie dokonał odprawy przez ponad tydzień. Oznacza to, że albo utknął i nie szukał pomocy, albo zlepia wiele funkcji w jednym dużym meldunku. (Pominąłem najgorszy scenariusz, on po prostu nic nie robi. Łatwo to rozwiązać ... dwa słowa brzmią, jakbyś był zatrudniony.)

Jeśli dokonujesz dużej odprawy, tracisz wiele korzyści z SCM, takich jak możliwość połączenia zestawu zmian z określoną funkcją. Prawdopodobnie będziesz mieć wiele do zrobienia i nie zawsze jest to łatwe.

Michael Brown
źródło
1
Nie robienie niczego nie zawsze jest najgorszym scenariuszem. Czasami lepiej jest napisać go od zera niż kod do przepisania ...
Malachi
4

Obecnie pracuję z fragmentem starszego kodu i uwielbiam sposób, w jaki poprzedni programista otrzymuje pierwszy element listy:

String result;
for(int i = 0; i < someList.size(); i++) {
    result = someList.get(i);
    break;
}

Ale najgorsze, co widziałem w tym kodzie, to definiowanie klas wbudowanych stron JSP, pisanie całego HTML, CSS i Javascript za pomocą skryptletów i out.println :-(

SourceRebels
źródło
4

Jednym z najgorszych zachowań anty-wzorcowych, jakie widziałem, był sklep, który pozwalał na wpisanie kodu do kontroli wersji dopiero po wyprodukowaniu. W połączeniu z wyłącznymi kasami w VSS, doprowadziło to do skomplikowanego procesu cofania kas, jeśli wada produkcyjna musiała zostać naprawiona przed następną wersją, nie mówiąc już o tym, że więcej niż jedna osoba musi zmienić dany plik na nadchodzące wydanie.

Fakt, że była to polityka departamentalna, sprawił, że stało się to jeszcze gorsze niż przypadek takiego zachowania jednego dewelopera.

Malachiasza
źródło
3

Blokowanie dosłownego napisu

synchronized("one") { /* block one A*/ }

synchronized("one") { /* block one B*/ }

Bardzo długie nazwy klas. (w JRE)

com.sun.java.swing.plaf.nimbus.
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState

Zła struktura dziedziczenia

com.sun.corba.se.internal.Interceptors.PIORB extends
com.sun.corba.se.internal.POA.POAORB extends
com.sun.corba.se.internal.iiop.ORB extends
com.sun.corba.se.impl.orb.ORBImpl extends
com.sun.corba.se.spi.orb.ORB extends
com.sun.corba.se.org.omg.CORBA.ORB extends 
org.omg.CORBA_2_3.ORB extends
org.omg.CORBA.ORB

Wyjątek, który nie jest.

public interface FlavorException { }

Bezcelowa i tajemnicza obsługa błędów

if (properties.size() > 10000)
   System.exit(0);

Niepotrzebne tworzenie obiektów

Class clazz = new Integer(0).getClass();
int num = new Integer(text).intValue();

Zgłaszanie wyjątku do innych celów

try {
    Integer i = null;
    Integer j = i.intValue();
} catch (NullPointerException e) {
    System.out.println("Entering "+e.getStackTrace()[0]);
}

Używanie obiektów instancji do metod statycznych.

Thread.currentThread().sleep(100);

Synchronizacja na polu innym niż końcowy

synchronized(list) {
   list = new ArrayList();
}

Bezcelowa kopia stałego ciągu znaków

String s = new String("Hello world");

Bezcelowe wywołanie String.toString ()

String s = "Hello";
String t = s.toString() + " World";

Wywołania do System.gc () w celu zwolnienia pamięci

Ustawienie zmiennej lokalnej na null, aby zwolnić trochę pamięci

    // list is out of scope anyway.
    list = null;
}

Używanie ++ i zamiast i ++ ze względu na wydajność (lub dowolną inną mikro-mikrooptymalizację)

Peter Lawrey
źródło
Niekoniecznie są to anty-wzory, tylko złe kodowanie.
Gary Willoughby,
@Gary lub złe wzorce rozwoju, które powtarzają się. Być może nie w ścisłej definicji anty-wzoru.
Peter Lawrey,
1
@Peter: „Wywołania do System.gc () w celu zwolnienia pamięci”, widziałem jeden przypadek, w którym .NET wybuchłby z wyjątkiem OutOfMemory, chyba że GC został wywołany jawnie. Oczywiście był to raczej wyjątek niż reguła.
Koder
3

Kod posypany:

// TODO: 

lub

// TODO: implement feature 

bez dodatkowych informacji.

Malachiasz
źródło
2

Iterator dla manekinów:

Iterator iCollection = collection.iterator();
for(int i = 0 ; i < collection.size() ; i++ ){
    if(collection.get(i) == something){
       iCollection.remove();
    }
 }

Fabryka Singletono:

public class SomeObject{
   private SomeObject() {}
   public static SomeObject getInstance(){
       return new SomeObject();
   }
}

rozwój oparty na if-else (zwany również zasadą otwartego zamknięcia - otwarty na modyfikację zamknięty dla zrozumienia):

if (sth1){
...  
}else if(sth2){
..
}
...
..
else if(sth1000000000000){
...
}

StringPattern (zwany także StringObject):

a) sendercode
if(sth1)
   str+="#a";
if(sth2)
   str+="#b";
...
if(sth1000)
   str+="#n";

b) receiver
   regexp testing if str contains #a, #b, ... #n
Marcin Michalski
źródło
Jest else ifto jednak często jedyny sposób na załatwienie sprawy. Myślę o strunach; nie mogę użyć przełącznika. Warunki powinny być podobne, aby były użyteczne.
Michael K
IMHO każdy, jeśli / inny można zastąpić prostym mapowaniem lub wzorem gości. W przypadku ciągów czyli może masz plik właściwości, takie jak: someString1 = some.package.ObjectToHandleSomeString1
Marcin Michalski
2
Singleton to fabryka . Nie ma nic złego w tym kodzie. Jedyną rzeczą, która może być błędna, jest to, że kod zewnętrzny zakłada, że ​​każde wywołanie getInstancezwraca tę samą instancję. Takie założenie złamałoby enkapsulację i jest w rzeczywistości jednym z najczęstszych anty-wzorów.
back2dos,
właśnie dlatego jest to mylące :) gdyby metoda została wywołana create () lub zwróciłaby tę samą instancję za każdym razem, gdy wszystko byłoby w porządku
Marcin Michalski,
2

Nie jest to tak naprawdę wzór kodowania, ale wzór behawioralny, jest jednak dość zły: modyfikowanie czegoś w kodzie (powiedzmy, że wymagania się zmieniły), a następnie dostosowywanie wszystkich testów jednostkowych, aż kod przejdzie. Ulepszenie lub po prostu usunięcie całego kodu testowego z metody testowej, ale pozostawienie tej metody.

Jest to powiązane z bardziej ogólnym wzorcem That will do , oto reprezentatywna linia kodu:

int num = new Integer( stringParam ).parseInt( stringParam );

W końcu to działa.

biziclop
źródło
2

Absolutnie nienawidzę odwracania abstrakcji lub ponownego wymyślania prymitywów niskiego poziomu na rzecz prymitywów wysokiego poziomu. Czasami jednak jest to spowodowane przez złych projektantów języka, a nie złych programistów. Przykłady:

  1. Użycie jednej klasy metody bez zmiennych składowych i odpowiedniego interfejsu (zaimplementowanej w postaci tabel wskaźników funkcji) zamiast wskaźnika funkcji. Pamiętaj, że w językach takich jak Java możesz nie mieć wyboru.

  2. W MATLAB i R nacisk kładziony jest na to, że wszystko jest wektorem / macierzą, a nie prymitywem.

  3. Kiedy walimy MATLAB, co powiesz na fakt, że nie ma liczb całkowitych, więc kiedy potrzebujesz liczby całkowitej, musisz użyć podwójnej.

  4. Czysto funkcjonalne języki, w których musisz używać wywołań funkcji do napisania pętli.

dsimcha
źródło
1

To byłoby zdecydowanie kopiowanie / wklejanie, widziałem wiele złego kodu z tego powodu, to i kodowanie kowboja i wszystko, co z tego wynika. (Boskie klasy, bardzo duże metody, błędne algorytmy itp.)

A jeśli wzorce projektowe są dozwolone, powiedziałbym: Wykonanie projektu jako zestawu działań z planu, bez przeprowadzania analizy projektu lub domeny.

Coyote21
źródło
1

Fasola java wielokrotnego użytku -

Fasola java z wieloma zmiennymi, używana w kilku różnych rodzajach operacji. Każda operacja używa dowolnego dowolnego podzbioru zmiennych komponentu bean i ignoruje inne. Kilka zmiennych dla stanu gui, kilka zmiennych wprowadzanych do przekazywania między komponentami, kilka, które prawdopodobnie nawet nie są już używane. Najlepsze przykłady nie mają dokumentacji, która utrudniałaby uznanie wzoru.

Nie można też zapomnieć o ukochanej

try{ 
   ... //many lines of likely dangerous operations
}
catch(Exception e){}
Steve B.
źródło
IMHO, wyjątki śmierdzą. Są zbyt trudne, aby je właściwie naprawić i dają fałszywe poczucie bezpieczeństwa.
Koder
Bez względu na to, co myślisz o cuchnącym wyjątku, połykając w ciszy, musisz śmierdzieć bardziej.
Steve B.
1

Były współpracownik miał zwyczaj ponownego wykorzystywania obiektów przez nadpisywanie ich właściwości, zamiast po prostu tworzenia nowych. Nigdy nie wyobrażałem sobie problemów, jakie spowodowało to przy wdrażaniu nowych funkcji.

deltreme
źródło
1

Coś, co wywołało u mnie wiele żalu, to wzór „Duża mapa na niebie”. Rzucanie po mapie zamiast używania odpowiednich obiektów. Nie masz pojęcia, jakie „zmienne” zawiera bez debugowania i nie wiesz, co może zawierać bez śledzenia kodu wstecz. Zazwyczaj odwzorowuje ciągi na obiekty lub ciągi na ciągi, które potencjalnie mają zostać przetworzone na prymitywy.

Buhb
źródło
Brzmi trochę jak poleganie na Session i ViewState w ASP.NET w celu przekazywania ogromnych ilości danych między stronami, co niestety jest często wymagane ...
Wayne Molina
1

Jednym z moich ulubionych anty-wzorców programistycznych jest użycie „projektu” bazy danych, który wymaga ciągłego dodawania dodatkowych kolumn do jednej lub więcej tabel programowo . Jest to kuzyn „projektu”, który tworzy nową tabelę dla każdej instancji bytu. Oba nieuchronnie uderzają w ograniczenia serwera bazy danych, ale zwykle dopiero, gdy system jest produkowany od dłuższego czasu.

Malachiasza
źródło
0

Myślę, że jednym z najgorszych anty-wzorów, jakie widziałem, jest użycie tabeli bazy danych jako pamięci tymczasowej zamiast użycia pamięci komputera.

Domena problemowa jest zastrzeżona, co uniemożliwia mi jej wyjaśnienie, ale nie jest konieczne zrozumienie podstawowego problemu. To była aplikacja GUI napisana w Javie z backendową bazą danych. Miał pobrać określone dane wejściowe, zmanipulować je, a następnie zatwierdzić przetworzone dane w bazie danych.

Nasz projekt ma dość skomplikowany algorytm, który zapisuje wartości pośrednie do późniejszego przetworzenia. Zamiast enkapsulować obiekty tymczasowe w ... obiektach, utworzono tabelę bazy danych, taką jak „t_object”. Za każdym razem, gdy wartość była obliczana, była dodawana do tej tabeli. Po zakończeniu działania algorytmu wybierze wszystkie wartości pośrednie i przetworzy je wszystkie w jednym dużym obiekcie Map. Po zakończeniu całego przetwarzania pozostałe wartości, które zostały oznaczone do zapisania, zostaną dodane do rzeczywistego schematu bazy danych, a tymczasowe wpisy w tabeli „t_object” zostaną odrzucone.

Tabela była również używana jako unikalna lista, dane mogły istnieć tylko raz. Mogłoby to być przyzwoitą cechą projektu, gdybyśmy wdrożyli Ograniczenia na stole, ale zakończyliśmy iterację po całej tabeli, aby sprawdzić, czy dane istnieją, czy nie. (Nie, nawet nie korzystaliśmy z zapytań, w których zastosowano klauzule where z CONTAINS)

Niektóre problemy, na które natrafiliśmy w związku z tym projektem, były w szczególności debugowaniem. Aplikacja została zbudowana w celu potokowania danych, aby istniało wiele GUI, które wstępnie przetwarzałyby dane, zanim dotrą do tego algorytmu. Proces debugowania polegał na przetworzeniu przypadku testowego, a następnie wstrzymaniu natychmiast po zakończeniu powyższej sekcji. Następnie zapytalibyśmy bazę danych, aby zobaczyć, jakie dane były zawarte w tej tabeli.

Innym problemem, który odkryliśmy, było to, że dane nie były poprawnie usuwane z tej tabeli tymczasowej, co w przyszłości mogłoby zakłócać działanie programu. Odkryliśmy, że było to spowodowane tym, że wyjątki nie były odpowiednio obsługiwane, a zatem aplikacja nie wychodzi poprawnie i nie usuwa danych z tabeli, nad którą sprawuje kontrolę.

Gdybyśmy użyli podstawowego projektowania obiektowego i zachowali wszystko w pamięci, powyższe problemy nigdy by nie wystąpiły. Po pierwsze, debugowanie byłoby proste, ponieważ moglibyśmy łatwo ustawić punkty przerwania w aplikacji, a następnie sprawdzić pamięć w stosie i stercie. Po drugie, po nieprawidłowym wyjściu z aplikacji pamięć java zostałaby naturalnie wyczyszczona bez obawy o usunięcie jej z bazy danych.

UWAGA: Nie twierdzę, że ten wzorzec jest z natury zły, ale w tym przykładzie uznałem, że nie jest potrzebny, gdy wystarczyłyby podstawowe zasady OO.

Nie jestem pewien nazwy tego anty-wzoru, ponieważ po raz pierwszy widziałem coś takiego. Jakieś dobre imiona, które możecie wymyślić dla tego wzoru?

jluzwick
źródło
Nie nazwałbym tego problemem powszechnym. Zależy to od tego, ile tymczasowych danych jest przechowywanych i co się z nimi dzieje.
GrandmasterB
Powinienem dodać więcej do postu, ale głównym problemem było to, że zamiast modyfikować obiekty i uzyskiwać dostęp do danych z pamięci, manipulowano nimi za pomocą kodu hack sql. Doprowadziło to do większej złożoności i poważnych problemów z wydajnością.
juzuzwick
2
Nie wspominasz o swojej problematycznej domenie, ale nie zawsze jest to zła rzecz, utrzymywanie stanu w innym miejscu, w ten sposób, może pozwolić na skalowanie procesów i introspekcję w długim okresie czasu, a także pomóc w debugowaniu. Czy dostęp do bazy danych był przyczyną problemów lub problemów z wydajnością?
Jé Queue
Zredagowałem ten artykuł, aby lepiej odzwierciedlić sposób, w jaki został wykorzystany w naszym projekcie, ale mieliśmy wiele problemów podczas debugowania, szczególnie dlatego, że musieliśmy zapytać bazę danych, aby zobaczyć zmienne tymczasowe. Mieliśmy również duże problemy związane z wydajnością, ponieważ baza danych była ciągle pytana o dane i sprawdzała, czy nowe dane już istnieją.
juzuzwick
0

Cart-Before-The-Horse - alias YouMightNeedIt

Na przykład:

  • Tworzenie schematu RDMB z abstrakcyjnymi pojęciami, odsyłaczami - w gruncie rzeczy nadmiernie uogólnioną kostką danych ... A NASTĘPNIE zapisywanie cechuje model do wszystkiego.
Sheldon Warkentin
źródło
To byłby zły brat bliźniak YAGNI, prawda?
Wayne Molina,
0

IMO najgorszym anty-wzorem, jaki widziałem, jest „Nie potrzebujemy żadnych śmierdzących wzorów”. Anty-wzór: Idea, że ​​wzorce projektowe są stratą czasu i możesz szybciej pisać kod, po prostu rozrzucając go i kopiując / wklejanie w razie potrzeby.

Wyróżnienie dotyczy kodu, który ładuje obiekt z bazy danych przy użyciu starego stylu VB6:

Foobar oFoo = new Foobar();
oFoo.FooID = 42;
if (oFoo.Load()) { 
    // do something with oFoo
}

nie jest to właściwie anty-wzór, ale pokazuje brak korzystania z właściwej architektury i oddzielenia problemów.

Ponadto takie rzeczy:

// this name is misleading, we may not always want to stand in fire,
// we may want to stand in slime or voidzones or ice patches...
public Foobar StandInFire() { }

// why is this here???
public string BeatWithNerfBat(string whom) { }

// ????
public int GivePony(string to) { }
Wayne M.
źródło