Extinderea unei structuri. Utilizarea claselor ajutătoare în loc de compoziție (Inginerie software, C#, Modele De Design, Orientat Pe Obiecte, Compoziție)

Nathan Cooper a intrebat.

Am un struct la care vreau să adaug metode statice. Da, ați ghicit bine, mă refer la Datetime.

Este o cerință destul de tipică pentru a adăuga MyCustomParse. Se întâmplă ca aceasta să returneze Datetime?, , deci nu este un constructor, dar se află în acea zonă, o metodă statică care returnează o instanță. Conținutul acestei metode a fost deja scris și în prezent fericit trăiește ca o metodă de instanță a unei clase care o folosește. Vreau să o scot din acea clasă pentru a o putea folosi în altă parte.

În mod tradițional, aș fi pus-o într-o clasă clasă ajutătoare (ceea ce am făcut aici), dar se pare că acestea sunt malefice.

Ceea ce vreau să fac cu adevărat este să leg Datetime.MyCustomParse(mystring). Dar singura cale pe care o văd este să fac o structură nouă care să compună Datetime. Apoi adaug metoda mea, scriu conversiile implicite și apoi pun toate celelalte structuri. Datetime metode etc. Acest lucru ar fi o pacoste de codat (Faptul de a fi verbos cauzează, de asemenea, probleme în ceea ce privește documentele xml și măsurătorile naive de acoperire a codului, dacă asta contează).

Am luat în considerare metode de extensie, , dar nu cred că metodele au sens în cazul instanțelor.

Care este poziția OO strictă? Primesc un permis pentru a scrie metode care ar trebui să aparțină claselor în clase de ajutor separate? atunci când vreau să extind structurile în acest mod?

Care este poziția pragmatică? (nu spun că OO nu este pragmatică), este acesta codul pe care l-ați scrie sau mi-a scăpat un truc?

internal static class DateTimeHelper
{

    public static DateTime? MyCustomParse(string utcDateTimeText, string[] formats)
    {
        DateTime utcDateTime;
        if (DateTime.TryParseExact(utcDateTimeText, formats, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out utcDateTime))
        {
            return utcDateTime;
        }

        return null;
    }
}

PS Știu că există puncte de vedere mai nuanțate cu privire la clasele helper, dar tot simt că trebuie să justific utilizarea mea aici.

Comentarii

  • Umm ce? DateTime are deja o clasă Parse… –  > Por Telastyn.
  • @Telastyn. Da, înțeleg că nu e clar. Ar trebui să-l redenumesc dacă l-aș băga cu DateTime. Am numit la MyCustomParse pentru a preveni confuzia. –  > Por Nathan Cooper.
  • Traditionally I would put it in a helper class (which is what I've done here), but apparently they're evil. Cred că ți-ai creat o problemă dacă ai crezut în această opinie. Dacă aveți un calcul utilizat frecvent care implică o anumită clasă, dar care nu are nevoie de acces la starea privată a niciunei clase, este logic să fie o metodă statică. De ce să introduceți o metodă izomorfă de fiecare dată când doriți o nouă funcție ajutătoare și apoi să treceți prin cercuri pentru a face conversia înainte și înapoi? De asemenea, rețineți că o metodă de extensie este doar un zahăr sintactic. –  > Por Doval.
  • Clasele ajutătoare nu sunt rele. Articolul menționat folosește erori logice. Deci, într-adevăr NU este subiectul discuției. Dar totuși voi da un exemplu. Dacă doriți să extindeți funcționalitatea pentru clasele neextensibile, de exemplu, String, clasele de ajutor sunt singura soluție. Oh, de la Java 8 pot servi și interfețele. Este doar un singur exemplu, dar există multe dintre ele. –  > Por Gangnus.
  • Helperii ca loc în care se înghesuie codul care nu se potrivește în altă parte ar putea fi descris ca fiind mai puțin ideal (nu sunt sigur că „rău” funcționează aici). Acesta este de obicei un indicator al unui design prost. Clasele care sunt pline de metode statice (așa cum autorul definește helperii) nu sunt rele, totuși. Ele sunt destul de utile în modelele de proiectare creativă, dar nu sunt strict necesare. – user22815
3 răspunsuri
radarbob

Care este poziția strictă OO?

„strict” este nedefinit în spațiul OO?

Care este poziția pragmatică? Vreau să o scot din acea clasă pentru a o putea folosi în altă parte. Conținutul acestei metode a fost deja scris și în prezent fericit trăiește ca o metodă de instanță a unei clase care o folosește.

Raționalizarea OO

Ei bine, o abordare ar putea fi să ne gândim la modele de proiectare creativă. Văd acest lucru ca o contrapondere la ideea „clasei ajutătoare”. Văd „ajutătorul” ca pe un smorgasbord – incoerent în ceea ce privește proiectarea obiectelor de domeniu.

Dar o fabrică, un constructor etc. compune comportamentul în cadrul unui obiect – un lucru coerent cu domeniul. Care este diferența față de o clasă ajutătoare? Intenția.

Pragmatismul OO întâlnește intenția

Refaceți pentru a transforma metoda într-un delegat. Dacă este necesar, puteți declara delegatul la nivelul spațiului de nume.

Scrieți o instanță de construire ceva care să înregistreze/legă/ceea ce este metoda delegată. Mă aștept ca implementarea metodei să se afle în interiorul acestui lucru ceva.

Ceva de genuleste de a instanția obiecte complet realizate ale clasei/structurii. Ceva nu conține acele metode ajutătoare „aleatorii” specifice tipului (dar nu aparținând instanței).

Și, din cauza intenției, nu dorim abordarea pragmatică a unei metode statice de clasă – pe care ați respins-o deja.

Comentarii

  • Un obiect este exagerat pentru exemplul său. El nu are nevoie de nicio stare internă, are nevoie doar de 2 intrări și o ieșire. –  > Por Doval.
  • Deci… Renumiți DateTimeHelper în DateTimeFactory? –  > Por Nathan Cooper.
  • @NathanCooper În esență, aplicația dvs. DateTimeHelper se comportă exact ca un DateTimeFactory așa că aici, da, doar redenumiți-o, astfel încât intenția clasei să fie mai clară. Dacă clasa dvs. are mai multe ajutoare fără legătură între ele, ar trebui în schimb să separați metoda factory într-o clasă factory. Problema cu „ajutoarele” este că, de multe ori, acestea nu respectă principiul responsabilității unice, în timp ce o fabrică are responsabilitatea unică de a construi obiecte. Unii (printre care mă număr și eu) preferă instanțele de fabrică, dar alții (printre care mă număr și eu) vor crea în schimb metode statice de fabrică. YMMV. –  > Por cbojar.
Doval

Clasele ajutătoare pot fi un antimodel de organizare atunci când aveți niște clase generice Helpers în care aruncați fiecare funcție statică diversă pe care toate proiectele dvs. au folosit-o vreodată. Nu faceți asta. În orice caz, ele nu sunt rele. O metodă statică este locul potrivit pentru a plasa unele calcule utilizate frecvent care nu trebuie să se uite la starea privată a niciunui obiect. Funcții precum MyCustomParse nu aparțin cu adevărat niciunei clase – își poate face treaba fără să se uite la detaliile de implementare ale nimănui, așa că nu face parte din nicio abstracțiune dată. Este doar una dintre multele modalități pe care le puteți alege pentru a converti între două reprezentări ale datelor.

Crearea de clase de înfășurare doar pentru că „pare” mai mult OOP dacă funcția este atașată la o instanță de ceva vă va cauza dureri de cap atât dvs. cât și viitorilor mentenanți de cod. În afară de faptul că va trebui să faceți conversia între tipurile înfășurate și cele înfășurate, va trebui fie să editați clasele înfășurate, fie să creați altele noi de fiecare dată când veți avea nevoie de o nouă metodă de ajutor – iar în acest din urmă caz veți avea și mai multe conversii. Nu se adaptează.

Metodele de extensie vă pot permite să folosiți funcții de ajutor cu sintaxa apelurilor de metode, ceea ce poate fi util pentru Intellisense sau atunci când un apel de funcție arată mai bine în forma infixă. Acesta este un simplu zahăr sintactic; din punct de vedere semantic, nu există o diferență prea mare între metodele de extensie și metodele statice obișnuite.

jbriggs

De ce să nu folosiți o metodă de extensie?

public static class DateTimeExtensions
{
   public static DateTime? AsDateTime(this string value, string[] formats)
   {
       DateTime utcDateTime;

       if (DateTime.TryParseExact(utcDateTimeText, formats,CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out utcDateTime))
       {
            return utcDateTime;
       }

       return null;
   }
 }

Aceasta va extinde tipul string adăugând metoda AsDateTime.

string[] formats= {"M/d/yyyy h:mm:ss tt", "MM/dd/yyyy h:mm:ss tt" }; 
string unParsedDateTime = "05/01/2009 6:32:05 PM";

DateTime parsed = unParsedDateTime.AsDateTime(formats);
// or  
parsed = "05/01/2009 6:32:05 PM".AsDateTime(formats);

Comentarii

  • Acest răspuns este numai de cod. Răspunsurile care conțin doar cod sunt considerate de proastă calitate aici. Ați putea detalia cum funcționează soluția dumneavoastră ? –  > Por Tulains Córdova.