dažnai būna daug aiškesnis ir paprastesnis būdas apibūdinti programos elgesiui nei imperatyvus programavimas. Deklaratyvus reiškia, kad teiginiais (deklaracijomis) aprašome, KĄ kažkuri programos dalis turi daryti, tuo tarpu imperatyviame programavime rašomos instrukcijos, kurios aprašo, KAIP programos dalys atlieka savo užduotis. Dažniausiai susiduriame su pastaruoju būdu, tokios programavimo kalbos kaip C# ar Java yra imperatyvios. Geriausias deklaratyvaus programavimo pavyzdys – HTML, kur deklaracijomis aprašome puslapio išvaizdą, o tuo, kaip jį atvaizduoti, rūpinasi naršyklė.

.NET projektuotojai puikiai suvokė deklaratyvaus programavimo naudą ir pasistengė platformą padaryti kiek galima deklaratyvesnę. Pavyzdžiui, kurdami puslapį su ASP išdėstome jo komponentus ir tik tada kodu aprašome puslapio elgesį įvykus įvairiems įvykiams. Naujoji WPF technologija „Windows“ programoms kurti taip pat suprojektuota pagal šį modelį. Be abejonės, toks būdas yra daug natūralesnis ir suprantamesnis nei, pavyzdžiui, pranešimų eilės apdorojimas (su tuo nesusidūrę tikriausiai net sunkiai įsivaizduoja, ką tai reiškia), ką tekdavo daryti gūdžiais „WinAPI“ laikais, ar nuoseklus turinio generavimas naudojant PHP.

Su lyg kiekviena .NET versija, platforma darosi vis deklaratyvesnė. Jau pirmoji versija turėjo gana deklaratyvų būdą duomenims atvaizduoti – duomenų pririšimą (angl. data binding). Jei turime duomenų, kuriuos norime atvaizduoti, pavyzdžiui, iš duombazės gavome lentelę, kurią norime parodyti ASP puslapyje, kodas atrodytų maždaug taip:

Default.aspx:

 
<asp:GridView runat="server" ID="MyData" />

Default.aspx.cs:

 
DataTable results;
 
// Iš kažkur užpildome results;
 
MyData.DataSource = results;
MyData.DataBind();

Tai puikus deklaratyvaus programavimo pavyzdys – vietoj to, kad patys cikle generuotume lentelę su duomenimis, tiesiog nurodome, kad lentelėje norime matyti savo duomenis, aprašome lentelės išvaizdą ir leidžiame viskuo pasirūpinti platformai.
Vis dėlto, kitiems net ir tos kelios kodo eilutės atrodė daug, taigi antroji .NET versija pristatė deklaratyvų duomenų pririšimą (angl. declarative data binding). Dabar duomenų šaltinį galime nurodyti tiesiog aprašydami lentelę:

 
<asp:GridView ID="MyData" runat="server"
              DataSourceID="MyDataSource" 
/>
 
<asp:SqlDataSource ID="MyDataSource" runat="server"
                   DataSourceMode="DataReader"
                   ConnectionString="database=pubs;trusted_connection=yes"
                   SelectCommand="SELECT * FROM Authors" 
/>

Žinoma, kai duomenys gaunami sudėtingesniais būdais, protingiau pririšimą atlikti pirmuoju būdu. Tačiau deklaratyvus pririšimas labai praverčia paprastesniais atvejais. Pavyzdžiui, norime pateikti pasirinkimą iš kelių verčių, kurias laikyti duomenų bazėje tiesiog nėra prasmės, o įrašyti tiesiai į kodą neleidžia savigarba. Šiuo atveju galime susikurti XML failą su verčių sąrašais (gal tokių pasirinkimų yra keli) ir deklaratyviai jį pririšti prie pasirinkimų:

choices.xml:

 
<?xml version="1.0" encoding="utf-8" ?>
<Choices>
  <Countries>
    <Country id="1" name="United States" />
    <Country id="2" name="Lithuania" />
    <Country id="3" name="Spain" />
  </Countries>
</Choices>

Default.aspx:

 
<asp:DropDownList runat="server" ID="Country" 
                  DataSourceID="ChoiceXmlSource" 
                  DataValueField="id" DataTextField="name" 
/>
 
<asp:XmlDataSource runat="server" ID="ChoiceXmlSource" 
                   DataFile="~/choices.xml" XPath="//Countries/Country" 
/>

ASP.net pavyzdys
Ir visa tai atlikome neparašę nė vienos C# kodo eilutės! Jei pasirinkimai keičiasi labai retai arba niekada (kaip šiuo atveju), kad kiekvieną kartą nereiktų nuskaitinėti failo, galime įjungti kešavimą (angl. caching), tada šis sprendimas greičio atžvilgiu niekuo nenusileis statiniam verčių įrašymui į kodą. Šiuo atveju atskyrėme logiką nuo duomenų, ko pasėkoje gavome daug skaitomesnį ir lengviau palaikomą kodą.

Pati C# programavimo kalba taip pat turi deklaratyvaus programavimo įrankį – atributus. Atributas – tai tam tikra žymė „užkabinta“ ant kažkokios kalbos konstrukcijos, t.y. klasės, savybės, metodo, nario ir pan. Tai yra būdas susieti informaciją su šiomis kalbos konstrukcijomis, t.y. mes galime uždėti metodams ar savybėms tam tikrus apribojimus, arba nurodyti, kaip jiems elgtis. Dėka atributų, programavimo kalba tapo dinamiška – tai neabejotinai vienas revoliucingiausių dalykų atsiradusių su .NET platforma.

Patį paprasčiausią pavyzdį galima išvysti sukūrus naują ASP Web Service projektą. Jums bus sugeneruotas toks metodas:

[WebMethod]
public string HelloWorld()
{
    return "Hello World";
}

Kadangi šis metodas pažymėtas „WebMethod“ atributu, platforma sugeneruos WSDL dokumetą, su aprašymu kaip kviesti šį web metodą, o visi SOAP iškvietimai bus nukreipti į būtent šį metodą. Galima įsivaizduoti, kiek kodo reiktų prirašyti, jei norėtume visa tai padaryti patys.

Iš techninės pusės, atributai yra klasės, paveldėtos iš Attribute bazinės klasės, o atributo uždėjimas yra tos klasės objekto sukūrimas. Šis objektas yra susiejamas su jo aprašoma konstrukcija, o šį ryšį galime pamatyti pasinaudodami .NET Reflection biblioteka. Taigi kurti savo atributus yra nepaprastai lengva.

Atributų panaudojimas labai platus. Štai pavyzdys iš realaus pasaulio: norime, kad keli klasės nariai būtų susieti su kažkokiomis XML failo šakomis. Tai atlikti nieko nežinant apie atributus, arba kalboje, kuri jų nepalaiko, būtų gana nelengva. Tikriausiai sukurtume bendrą failo skaitymo metodą ir jį kviestume kiekvieno nario kode, ten pat nurodydami, kokią failo dalį šis narys atitinka, kodas būtų maždaug toks:

 
private string GetValueFromXML(string xpath)
{
    // implementacija praleista ...
}
 
private string _timeZone = null;
public string TimeZone
{
    get 
    {
 return _timeZone ?? 
                 (_timeZone = GetValueFromXML("/Config/TimeZone"));
    }
}

Žinoma, tokį kodą sunku skaityti, be to, kiekvienam nariui turėtume kartoti iš esmės tą patį kodą, o tai jau sako, kad kažkas su šituo sprendimu ne taip.

Savo įrankių dėžėje turėdami atributus, šį kodą galime padaryti skaitomesnį. Visų pirma aprašome paprastą atributą, kuris elgsis kaip žymeklis, nurodantis vietą XML faile (panaudosime keletą naujų C# 3.5 kalbos konstrukcijų):

 
class FromXmlAttribute : Attribute
{
    public string XPath { get; set; }
 
    public FromXmlAttribute(string xpath) {
        XPath = xpath;
    }
}

Tada savo klasę galime perrašyti taip:

 
class MyClass
{
    public MyClass()
    {
        // Čia pereiname per visus klasės narius,
	  // kurie pažymėti FromXml atributu ir priskiriame
        // jiems reikšmes, atsižvelgdami į jų kelius XML faile.  
    }
 
    [FromXml("/Config/TimeZone")]
    public string TimeZone { get; }
}

Tokį kodą suprasti ir prižiūrėti, be abejo, daug lengviau, nei ankstesnįjį. Be to, dabar daug mažesnė tikimybė kažkur įvelti klaidą, nes visas kodas sukoncentruotas vienoje vietoje. Analogiškai galėtume pasirašyti atributus, nurodančius, kad skaityti reikia, pavyzdžiui, iš duomenų bazės. Kadangi skaitymo logika paslėpta nuo klasės naudotojų, ateityje galėtume keisti duomenų šaltinius, pavyzdžiui, laiko zoną skaityti iš duomenų bazės. Klasės naudotojai pokyčio nepajaustų, o mums reiktų pakeisti tik atributą.

Galima sugalvoti daug atributų pritaikymo scenarijų, pavyzdžiui, galėtume susikurti atributą, kuris nurodytų, pagal kurį klasės narį reiktų rūšiuoti šios klasės objektus, jei jie būtų talpinami į kolekciją:

[SortOn("Name")]
class Student
{
    string Name { get; set; }
    string LastName { get; set; }
    string Phone { get; set; }
}

Žinoma, dar reiktų parašyti kodą, kuris atliktų patį objektų palyginimą ir rūšiavimą.

Dar viena populiari atributų panaudojimo sritis yra pačio kodo žymėjimas, t.y. galime nurodyti, kad metodas yra skirtas tik testavimui arba metodas yra pasenęs ir reiktų naudoti kažką kito ir pan. C/C++ kalbose tai daugmaž atitiktų makro direktyvas, kurios sutartinai laikomos blogiu.

Tarkime turime metodą, kuris į konsolės langą rašo dabartinę programos būklę. Programos kūrimo metu jį dažnai kviečiame, tačiau galutinėje versijoje visų šitų parodymų, žinoma, neturi būti. Standartinis C/C++ programuotojo sprendimas šiuo atveju būtų sąlyginė makro direktyva pačiame metode, mat kvietimų yra labai daug ir tikrinti prieš kiekvieną jų būtų tiesiog neprotinga:

private void ShowLog()
{
#if DEBUG
 
    Console.WriteLine("Debug stuff ...");
    // Kita informacija ...
 
#endif
}

Dabar galutinėje programos versijoje šis metodas bus tuščias. Vis dėlto liks daug šio metodo iškvietimų, o ar juos kompiliatorius išeliminuos – neaišku.

C# programuotojo sprendimas, žinoma, bus panaudoti standartinį .NET atributą:

[Conditional("DEBUG")]
private void ShowLog()
{
    Console.WriteLine("Debug stuff ...");
    // Kita informacija ...
}

Dabar ne tik aiškiau matyti metodo prasmė, bet ir galime būti tikri, kad šio metodo iškvietimas galutinėje programos versijoje neturės jokios įtakos, mat kompiliatorius pamatęs šį atributą tiesiog ignoruos visus šio metodo iškvietimus.

Tokios naujos technologijos .NET 3.5 versijoje, kaip LINQ, deklaratyvumą perkėlė į kitą lygį. Užtenka vos pažiūrėti į LINQ sakinį ir iš karto matosi, koks tai galingas ir ekspresyvus dalykas. Šiame pavyzdyje sujungėme du masyvus, kurie saugo klientų ir užsakymų sąrašus į vieną kolekciją su užsakymo tekstu:

var OrderList = from order in Orders
                join customer in Customers on order.c_id equals customer.id
                select new { 
                	  Text = customer.name + " ordered " + order.item 
                };

Jei ne LINQ, būtume turėję rašyti daug masyvų sujungimo kodo, kuris šiuo atveju nėra esminis – mes tiksliai žinome, kokį rezultatą norime matyti ir mums nelabai svarbu, kaip jis bus gautas. Panašia logika pagrįstas ir SQL. Apskritai, LINQ yra atskiro straipsnio verta tema, todėl daugiau į ją nesigilinsime.

Apibendrinant, yra galingas įrankis .NET programuotojo įrankių dėžėje. Teisingai panaudotas jis gali sutaupyti daug kodo rašymo bei apsaugoti nuo klaidų dabar ir ateityje. Deklaratyvus kodas yra daug suprantamesnis ir natūralesnis žmonėms, todėl turėtų būtų naudojamas kuo dažniau, kur tik galima savo ketinimus išreikšti deklaratyviai.

Panašūs straipsniai


“Deklaratyvus programavimas su .NET” komentarų: 4

  1. arturaz

    Na apie C# pasakyti daug negaliu, tačiau tas duomenų įkišimas į template yra gana pavyzdinis mėsmalės pavyzdys, kuomet po kurio laiko pasidaro nebeaišku iš kur turėtų ateiti tam tikri duomenys. Isivaizduok portalą su 500 duomenų šaltinių, vieni jų yra puslapyje, kiti kode, iš kur žinoti kur kuris yra? MVC šiuo atveju smarkiai apdeda .NET pasirinktą metodą. Ir aišku SQL’as templeituose irgi velniai žino ką veikia. Gaunasi standartinė PHP/ASP/JSP + HTML + SQL mėsmalė.

    Be to tavo vadinamas deklaratyvus programavimas (pirmoje straipsnio dalyje) visame pasaulyje žinomas kaip komponentinis programavimas ir, dėja, nėra panacėja (kaip minėjau savo straipsnyje, laisvės gauni tiek, kiek davė komponento kūrėjas). Kartais gerai, kartais blogai…

    O atributai tai geras dalykas, norėčiau, kad ruby tokį turėtų (nors kaip nekompiliuojamai kalbai tai turbūt nepavyks :))

  2. Saulius

    Na kas dėl pavadinimo, tai bent jau .NET pasaulyje, kiek knygose teko skaityti ir terminuose kaip “declarative data binding” matyti, šitas dalykas vadinasi “Declarative programming”, (wikipedia, beje, ir bendrą tokį įrašą turi http://en.wikipedia.org/wiki/Declarative_programming), o kaip tiksliau užlietuvinti, aš nežinojau. Ir šiaip, imho, component based programming tai ne visai tas pats.

    Kas dėl kitų komentarų: aspx peidžas biški skiriasi nuo php templeito, todėl vadint mėsmale nelabai gražu :) Šiaip galiu pasakyt iš savo patirties, kad kai duomenų source’as yra šalia komponento, tai susigaudyti daug lengviau, nei kai pririšimas yra kode, nes vat ten tai jis tikrai bilekur būna numestas.
    Kaip ir rašiau, deklaratyviai bindinti reikia tik smulkiais atvejais, kai duomenys yra labiau komponento propertis, o ne turinys (kaip kad mano pavyzdyje). Dėl sql’o ten tai irgi sutinku, čia buvo daugiau kodas pademonstruot idėjai, kuris irgi tiktų tik labai paprastiems atvejams.

  3. Sergejus

    O man straipsnis patiko, ypač kad paminėti LINQ ir WPF.

  4. 3

    Straipsnis visai nieko. Asmeniskai man tas deklaratyvusis programavimas nelabai prie sirdies. Mat tendencijos matosi tokios, kad ateityje “programuosime” diagramomis - tampydami su pele objektus ir ju atributus is vienos vietos i kita :-))
    Tipo pamazu is programuotoju metamorfozes budu virsime i grynai dizainerius.
    Man tas nepatinka, bo as noriu programinti, o ne dizaina daryti :-)

Rašyti komentarą

Jūs privalote prisijungti jeigu norite rašyti komentarą.