Незмі́нний об'є́кт (англ. Immutable object) — в об'єктно-орієнтованому програмуванні, об'єкт, стан якого не може бути змінено після створення. На противагу незмінним, стан змінних об'єктів може змінюватись після створення. Об'єкт може бути як незмінним повністю, так і певні його атрибути можуть бути задекларовані незмінними, використовуючи, наприклад, декларацію const мови програмування . У деяких випадках, об'єкт вважається незмінним навіть тоді, коли змінюються деякі його внутрішні атрибути, за умови, що зовні його стан виглядає незмінним. Наприклад, об'єкт, який використовує запам'ятовування результатів проміжних обчислень для кешування результатів складних обчислень, може вважатись незмінним. Початковий стан незмінного об'єкта, як правило, визначається , але, він може, також, визначатись безпосередньо перед використанням об'єкта.
Часто, незмінні об'єкти можуть бути корисними через те, що вони дозволяють уникнути деяких дорогих операцій копіювання та порівняння, полегшуючи, в такий спосіб, вихідний код програми, та прискорюючи її роботу. Однак, у деяких випадках, незмінність об'єкта може заважати, наприклад, якщо об'єкт містить велику кількість змінних даних. Через це, багато мов програмування мають можливості роботи як із змінними, так і з незмінними об'єктами.
Незмінні об'єкти часто бувають корисними завдяки тому, що вони по суті потоко-безпечні. Інша перевага в тому, що вони є простішими для розуміння і пропонують більш високий рівень безпеки, ніж змінювані об'єкти.
Реалізація
Незмінність об'єкту не означає, що фізична пам'ять в якій зберігається об'єкт не має доступу для запису. Скоріше, властивість бути незмінним йому задається під час компіляції у вигляді конструкції, яка вказує програмісту що можна робити з об'єктом через його інтерфейс, не зважаючи на те, що в абсолютній ситуації він може змінити його (наприклад, в обхід системи типів або порушуючи константність в C або ).
Ada
В Ada будь-який об'єкт оголошується або variable
(тобто змінним), або constant
(тобто незмінним) через ключове слово constant
.
type Some_type is new Integer; x: constant Some_type:= 1; -- незмінний y: Some_type; -- змінний
Параметри підпрограми є незмінними в режимі in
та змінними в режимах in out
і out
.
procedure Do_it(a: in Integer; b: in out Integer; c: out Integer) is begin -- а незмінний b:= b + a; c:= a; end Do_it;
C#
У майбутніх версіях C# можливо з'явиться ключове слово immutable
, за допомогою якого можна буде визначати незмінний тип на основі його сигнатури. Поки ж нам доводиться користуватися тим, що є.
Візьмемо для прикладу клас RGBColor
, що описує деякий колір в колірній моделі RGB:
public class RGBColor { public int Red { get; set; } public int Green { get; set; } public int Blue { get; set; } public RGBColor(int red = 0, int green = 0, int blue = 0) { Red = red; Green = green; Blue = blue; } }
Цей клас має автоматичні властивості як для читання, так і для запису і необов'язкові параметри, які дозволяють користувачу опускати значення для будь-яких компонентних кольорів.
Для перетворення даного класу в незмінний об’єкт перш за все зробимо його сетери приватними (private
)
public class RGBColor { public int Red { get; private set; } public int Green { get; private set; } public int Blue { get; private set; } public RGBColor(int red = 0, int green = 0, int blue = 0) { Red = red; Green = green; Blue = blue; } }
Тепер об'єкт не може бути змінений ззовні, однак всередині він так само може змінюватись. Необхідно позбутись від сеттерів і впровадити поля тільки для читання (readonly). Також змінимо будову конструктора, перетворивши його необов’язкові поля на обов’язкові.
public class RGBColor { private readonly int red; private readonly int green; private readonly int blue; public int Red => red; public int Green => green public int Blue => blue; public RGBColor(int red, int green, int blue) { this.red = red; this.green = green; this.blue = blue; } }
Отже, можна виділити 3 прості кроки для створення незмінного класу в С#:
- Видалити сетери.
- Перетворити приватні поля на readonly (або створити їх якщо вони не існують).
- Змінити конструктор так, щоб він вимагав всі необхідні параметри під час створення нового об’єкту .
C++
В C++, реалізація константності класу Cart
дозволено створювати нові екземпляри класу незмінними, з використанням ключового слова const
(незмінний) або змінним, за бажанням, забезпечуючи дві різні версії методу getItems()
. (Відмітимо, що в C++ це не обов'язково — і фактично не можливо — створити спеціалізований конструктор для незмінних екземплярів const
.)
template<typename T> class Cart { private: std::vector<T> items; public: Cart(const std::vector<T>& v): items(v) { } std::vector<T>& getItems() { return items; } const std::vector<T>& getItems() const { return items; } int total() const { /* повертає суму */ } };
Якби в класі було поле, яке є вказівником чи посиланням на інший об'єкт, то все одно можливо змінювати об'єкт, на який посилається такий вказівник або посилання в константному методі, без помилки порушення константності. Можна стверджувати, що об'єкт в такому випадку не є цілком незмінним. C++ також забезпечує абстрактну незмінність (на відміну від незмінності даних) за допомогою ключового слова mutable
, що дозволяє змінювати змінну член класу в середині методу, що оголошений як const
.
template<typename T> class Cart { private: std::vector<T> items; mutable int costInCents; mutable bool totaled; public: Cart(const std::vector<T>& v): items(v), totaled(false) { } const std::vector<T>& getItems() const { return items; } int total() const { if (!totaled) { costInCents = 0; for (std::vector<T>::const_iterator itor = items.begin(); itor != items.end(); ++itor) costInCents += itor->costInCents(); totaled = true; } return costInCents; } };
D
В мові D існують два класифікатори типу - const
та immutable
- для незмінних об'єктів . На відміну класифікаторів const
(C++), final
(Java) і readonly
(C#), вони є транзитивними і повторно застосовуваними до будь-якого типу, досяжного через посилання такої змінної. Різниця між const
і immutable
полягає в тому, для чого вони використовуються. const
є властивістю змінної: таким чином цілком коректно можуть існувати змінні посилання на згадане значення (referred value), тобто значення може насправді змінитися. На противагу const
, immutable
- це властивість згаданого значення (referred value), тобто значення і все транзитивно досяжне від нього не може змінюватися (без порушення системи типів, що може призвести до невизначеної поведінки). Будь-яке посилання на таке значення має бути позначене const
або immutable
. В основному для будь-якого невизначеного типу T
, const(T)
є непересічним об'єднанням T
(змінний) і immutable(T)
.
class C { /*змінний*/ Object mField; const Object cField; immutable Object iField; }
Java
Класичним прикладом незмінного об'єкта є екземпляр класу Java String
:
String s = "ABC"; s.toLowerCase();
Метод toLowerCase()
не змінює дані "ABC", що містяться в s
. Замість цього створюється новий об'єкт String, якому присвоюється значення "abc". Посилання на цей об'єкт String повертається методом toLowerCase()
. Для того, щоб String s
містив дані "abc", необхідний інший підхід:
s = s.toLowerCase();
Тепер String s
посилається на новий об'єкт String, який містить "abc". В синтаксисі оголошення класу String немає нічого, що могло б забезпечити його незмінність. Скоріше жоден з методів класу String ніколи не впливав на дані, які містить об'єкт String, що робить його незмінним.
Ключове слово final
використовується для реалізації незмінних примітивних типів і об'єктних посилань , але саме по собі воно не може зробити об'єкти незмінними. Наведемо нижче деякі приклади.
Примітивні типи (int
, long
, short
і т.д.) можуть бути перевизначені після першого визначення. Цього можна запобігти використовуючи ключове слово final
.
int i = 42; // int є примітивним типом i = 43; // OK. Код скомпілюється final int j = 42; j = 43; // код не скомпілюється. j з final не може бути перевизначеним
Посилальні типи не можна зробити незмінними використовуючи ключове слово final
. final
лише запобігає перепризначенню.
final MyObject m = new MyObject(); // m посилального типу m.data = 100; // OK. Ми можемо поміняти властивості об’єкта m (m змінний і final не змінює даний факт) m = new MyObject(); // не скомпілюється. m є final, тому не може бути перевизначеним
У загальному випадку незмінний об'єкт може бути створений шляхом визначення класу, який не має жодного з його членів, і не має будь-яких сеттерів. Наступний клас створить незмінний об'єкт:
class ImmutableInt { private final int value; public ImmutableInt(int i) { value = i; } public int getValue() { return value; } }
Як видно з наведеного вище прикладу, значення ImmutableInt
може бути встановлено тільки при створенні екземпляра об'єкта і при наявності тільки геттера (getValue
) стан об'єкта не може бути змінено після створення екземпляра.
Однак необхідно стежити за тим, щоб всі об'єкти, на які посилається даний об'єкт, також були незмінними. Наприклад, дозволяючи отримання посилання на масив або ArrayList
, які будуть отримані через геттер, ми можемо допустити зміну внутрішнього стану об’єкта шляхом зміни масиву або колекції:
class NotQuiteImmutableList<T> { private final List<T> list; public NotQuiteImmutableList(List<T> list) { // створює новий ArrayList і зберігає посилання на нього. this.list = new ArrayList(list); } public List<T> getList() { return list; } }
Проблема з наведеним вище кодом полягає в тому, що ArrayList
можна отримати за допомогою getList
і маніпулювати, що призведе до зміни стану самого об'єкта, тому він не є незмінним.
// notQuiteImmutableList містить "a", "b", "c" List<String> notQuiteImmutableList= new NotQuiteImmutableList(Arrays.asList("a", "b", "c")); // тепер список містить "a", "b", "c", "d" -- цей список змінний notQuiteImmutableList.getList().add("d");
Один із способів обійти цю проблему - повернути копію масиву або колекції при виклиці геттера :
public List<T> getList() { // повертає копію списку, тому внутрішній стан початкового списку не може бути змінено return new ArrayList(list); }
JavaScript
У JavaScript деякі вбудовані типи (числа, рядки) незмінні, але користувальницькі об'єкти, як правило, змінюються.
function doSomething(x) { /* чи поміняє х свій первинний стан? */ }; var str = 'a string'; var obj = { an: 'object' }; doSomething(str); // рядки, числа і логічні типи незмінні, функція отримує копію doSomething(obj); // об’єкти проходять за посиланням і змінні всередині функції doAnotherThing(str, obj); // `str` не поміняється, але `obj` може помінятись
Щоб імітувати незмінність в об'єкті, властивості можна визначити як read-only
(writable: false
)
var obj = {}; Object.defineProperty(obj, 'foo', { value: 'bar', writable: false }); obj.foo = 'bar2'; // ігнорується
Проте, наведений вище підхід дозволяє додавати нові властивості. Крім того, можна використовувати Object.freeze, щоб зробити існуючі об'єкти незмінними.
var obj = { foo: 'bar' }; Object.freeze(obj); obj.foo = 'bars'; // не можна змінити властивість, ігнорується obj.foo2 = 'bar2'; // не можна додати властивість, ігнорується
З моменту впровадження React, незмінний стан все частіше використовується в JavaScript-і, що сприяє поширення потоко подібних моделей, таких як Redux .
Примітки
- Goetz et al. Java Concurrency in Practice. Addison Wesley Professional, 2006, Section 3.4. Immutability
- Carlos Schults (March 2018). C# Immutable Types: Understanding the Attraction. ndepend.com.
- D Language Specification § 18
- How to create Immutable Class and Object in Java – Tutorial Example. Javarevisited.blogspot.co.uk. 4 березня 2013. Процитовано 14 квітня 2014.
- Неизменяемый класс?. qaru.site.
- Immutability in JavaScript: A Contrarian View. Desalasworks.
Див. також
Це незавершена стаття про інформаційні технології. Ви можете проєкту, виправивши або дописавши її. |
Вікіпедія, Українська, Україна, книга, книги, бібліотека, стаття, читати, завантажити, безкоштовно, безкоштовно завантажити, mp3, відео, mp4, 3gp, jpg, jpeg, gif, png, малюнок, музика, пісня, фільм, книга, гра, ігри, мобільний, телефон, android, ios, apple, мобільний телефон, samsung, iphone, xiomi, xiaomi, redmi, honor, oppo, nokia, sonya, mi, ПК, web, Інтернет
Nezmi nnij ob ye kt angl Immutable object v ob yektno oriyentovanomu programuvanni ob yekt stan yakogo ne mozhe buti zmineno pislya stvorennya Na protivagu nezminnim stan zminnih ob yektiv mozhe zminyuvatis pislya stvorennya Ob yekt mozhe buti yak nezminnim povnistyu tak i pevni jogo atributi mozhut buti zadeklarovani nezminnimi vikoristovuyuchi napriklad deklaraciyu const movi programuvannya C U deyakih vipadkah ob yekt vvazhayetsya nezminnim navit todi koli zminyuyutsya deyaki jogo vnutrishni atributi za umovi sho zovni jogo stan viglyadaye nezminnim Napriklad ob yekt yakij vikoristovuye zapam yatovuvannya rezultativ promizhnih obchislen dlya keshuvannya rezultativ skladnih obchislen mozhe vvazhatis nezminnim Pochatkovij stan nezminnogo ob yekta yak pravilo viznachayetsya ale vin mozhe takozh viznachatis bezposeredno pered vikoristannyam ob yekta Chasto nezminni ob yekti mozhut buti korisnimi cherez te sho voni dozvolyayut uniknuti deyakih dorogih operacij kopiyuvannya ta porivnyannya polegshuyuchi v takij sposib vihidnij kod programi ta priskoryuyuchi yiyi robotu Odnak u deyakih vipadkah nezminnist ob yekta mozhe zavazhati napriklad yaksho ob yekt mistit veliku kilkist zminnih danih Cherez ce bagato mov programuvannya mayut mozhlivosti roboti yak iz zminnimi tak i z nezminnimi ob yektami Nezminni ob yekti chasto buvayut korisnimi zavdyaki tomu sho voni po suti potoko bezpechni Insha perevaga v tomu sho voni ye prostishimi dlya rozuminnya i proponuyut bilsh visokij riven bezpeki nizh zminyuvani ob yekti RealizaciyaNezminnist ob yektu ne oznachaye sho fizichna pam yat v yakij zberigayetsya ob yekt ne maye dostupu dlya zapisu Skorishe vlastivist buti nezminnim jomu zadayetsya pid chas kompilyaciyi u viglyadi konstrukciyi yaka vkazuye programistu sho mozhna robiti z ob yektom cherez jogo interfejs ne zvazhayuchi na te sho v absolyutnij situaciyi vin mozhe zminiti jogo napriklad v obhid sistemi tipiv abo porushuyuchi konstantnist v C abo C Ada V Ada bud yakij ob yekt ogoloshuyetsya abo variable tobto zminnim abo constant tobto nezminnim cherez klyuchove slovo constant type Some type is new Integer x constant Some type 1 nezminnij y Some type zminnij Parametri pidprogrami ye nezminnimi v rezhimi in ta zminnimi v rezhimah in out i out procedure Do it a in Integer b in out Integer c out Integer is begin a nezminnij b b a c a end Do it C U majbutnih versiyah C mozhlivo z yavitsya klyuchove slovo immutable za dopomogoyu yakogo mozhna bude viznachati nezminnij tip na osnovi jogo signaturi Poki zh nam dovoditsya koristuvatisya tim sho ye Vizmemo dlya prikladu klas RGBColor sho opisuye deyakij kolir v kolirnij modeli RGB public class RGBColor public int Red get set public int Green get set public int Blue get set public RGBColor int red 0 int green 0 int blue 0 Red red Green green Blue blue Cej klas maye avtomatichni vlastivosti yak dlya chitannya tak i dlya zapisu i neobov yazkovi parametri yaki dozvolyayut koristuvachu opuskati znachennya dlya bud yakih komponentnih koloriv Dlya peretvorennya danogo klasu v nezminnij ob yekt persh za vse zrobimo jogo seteri privatnimi private public class RGBColor public int Red get private set public int Green get private set public int Blue get private set public RGBColor int red 0 int green 0 int blue 0 Red red Green green Blue blue Teper ob yekt ne mozhe buti zminenij zzovni odnak vseredini vin tak samo mozhe zminyuvatis Neobhidno pozbutis vid setteriv i vprovaditi polya tilki dlya chitannya readonly Takozh zminimo budovu konstruktora peretvorivshi jogo neobov yazkovi polya na obov yazkovi public class RGBColor private readonly int red private readonly int green private readonly int blue public int Red gt red public int Green gt green public int Blue gt blue public RGBColor int red int green int blue this red red this green green this blue blue Otzhe mozhna vidiliti 3 prosti kroki dlya stvorennya nezminnogo klasu v S Vidaliti seteri Peretvoriti privatni polya na readonly abo stvoriti yih yaksho voni ne isnuyut Zminiti konstruktor tak shob vin vimagav vsi neobhidni parametri pid chas stvorennya novogo ob yektu C V C realizaciya konstantnosti klasu Cart dozvoleno stvoryuvati novi ekzemplyari klasu nezminnimi z vikoristannyam klyuchovogo slova const nezminnij abo zminnim za bazhannyam zabezpechuyuchi dvi rizni versiyi metodu getItems Vidmitimo sho v C ce ne obov yazkovo i faktichno ne mozhlivo stvoriti specializovanij konstruktor dlya nezminnih ekzemplyariv const template lt typename T gt class Cart private std vector lt T gt items public Cart const std vector lt T gt amp v items v std vector lt T gt amp getItems return items const std vector lt T gt amp getItems const return items int total const povertaye sumu Yakbi v klasi bulo pole yake ye vkazivnikom chi posilannyam na inshij ob yekt to vse odno mozhlivo zminyuvati ob yekt na yakij posilayetsya takij vkazivnik abo posilannya v konstantnomu metodi bez pomilki porushennya konstantnosti Mozhna stverdzhuvati sho ob yekt v takomu vipadku ne ye cilkom nezminnim C takozh zabezpechuye abstraktnu nezminnist na vidminu vid nezminnosti danih za dopomogoyu klyuchovogo slova mutable sho dozvolyaye zminyuvati zminnu chlen klasu v seredini metodu sho ogoloshenij yak const template lt typename T gt class Cart private std vector lt T gt items mutable int costInCents mutable bool totaled public Cart const std vector lt T gt amp v items v totaled false const std vector lt T gt amp getItems const return items int total const if totaled costInCents 0 for std vector lt T gt const iterator itor items begin itor items end itor costInCents itor gt costInCents totaled true return costInCents D V movi D isnuyut dva klasifikatori tipu const ta immutable dlya nezminnih ob yektiv Na vidminu klasifikatoriv const C final Java i readonly C voni ye tranzitivnimi i povtorno zastosovuvanimi do bud yakogo tipu dosyazhnogo cherez posilannya takoyi zminnoyi Riznicya mizh const i immutable polyagaye v tomu dlya chogo voni vikoristovuyutsya const ye vlastivistyu zminnoyi takim chinom cilkom korektno mozhut isnuvati zminni posilannya na zgadane znachennya referred value tobto znachennya mozhe naspravdi zminitisya Na protivagu const immutable ce vlastivist zgadanogo znachennya referred value tobto znachennya i vse tranzitivno dosyazhne vid nogo ne mozhe zminyuvatisya bez porushennya sistemi tipiv sho mozhe prizvesti do neviznachenoyi povedinki Bud yake posilannya na take znachennya maye buti poznachene const abo immutable V osnovnomu dlya bud yakogo neviznachenogo tipu T const T ye neperesichnim ob yednannyam T zminnij i immutable T class C zminnij Object mField const Object cField immutable Object iField Java Klasichnim prikladom nezminnogo ob yekta ye ekzemplyar klasu Java String String s ABC s toLowerCase Metod toLowerCase ne zminyuye dani ABC sho mistyatsya v s Zamist cogo stvoryuyetsya novij ob yekt String yakomu prisvoyuyetsya znachennya abc Posilannya na cej ob yekt String povertayetsya metodom toLowerCase Dlya togo shob String s mistiv dani abc neobhidnij inshij pidhid s s toLowerCase Teper String s posilayetsya na novij ob yekt String yakij mistit abc V sintaksisi ogoloshennya klasu String nemaye nichogo sho moglo b zabezpechiti jogo nezminnist Skorishe zhoden z metodiv klasu String nikoli ne vplivav na dani yaki mistit ob yekt String sho robit jogo nezminnim Klyuchove slovo final vikoristovuyetsya dlya realizaciyi nezminnih primitivnih tipiv i ob yektnih posilan ale same po sobi vono ne mozhe zrobiti ob yekti nezminnimi Navedemo nizhche deyaki prikladi Primitivni tipi int long short i t d mozhut buti pereviznacheni pislya pershogo viznachennya Cogo mozhna zapobigti vikoristovuyuchi klyuchove slovo final int i 42 int ye primitivnim tipom i 43 OK Kod skompilyuyetsya final int j 42 j 43 kod ne skompilyuyetsya j z final ne mozhe buti pereviznachenim Posilalni tipi ne mozhna zrobiti nezminnimi vikoristovuyuchi klyuchove slovo final final lishe zapobigaye perepriznachennyu final MyObject m new MyObject m posilalnogo tipu m data 100 OK Mi mozhemo pominyati vlastivosti ob yekta m m zminnij i final ne zminyuye danij fakt m new MyObject ne skompilyuyetsya m ye final tomu ne mozhe buti pereviznachenim U zagalnomu vipadku nezminnij ob yekt mozhe buti stvorenij shlyahom viznachennya klasu yakij ne maye zhodnogo z jogo chleniv i ne maye bud yakih setteriv Nastupnij klas stvorit nezminnij ob yekt class ImmutableInt private final int value public ImmutableInt int i value i public int getValue return value Yak vidno z navedenogo vishe prikladu znachennya ImmutableInt mozhe buti vstanovleno tilki pri stvorenni ekzemplyara ob yekta i pri nayavnosti tilki gettera getValue stan ob yekta ne mozhe buti zmineno pislya stvorennya ekzemplyara Odnak neobhidno stezhiti za tim shob vsi ob yekti na yaki posilayetsya danij ob yekt takozh buli nezminnimi Napriklad dozvolyayuchi otrimannya posilannya na masiv abo ArrayList yaki budut otrimani cherez getter mi mozhemo dopustiti zminu vnutrishnogo stanu ob yekta shlyahom zmini masivu abo kolekciyi class NotQuiteImmutableList lt T gt private final List lt T gt list public NotQuiteImmutableList List lt T gt list stvoryuye novij ArrayList i zberigaye posilannya na nogo this list new ArrayList list public List lt T gt getList return list Problema z navedenim vishe kodom polyagaye v tomu sho ArrayList mozhna otrimati za dopomogoyu getList i manipulyuvati sho prizvede do zmini stanu samogo ob yekta tomu vin ne ye nezminnim notQuiteImmutableList mistit a b c List lt String gt notQuiteImmutableList new NotQuiteImmutableList Arrays asList a b c teper spisok mistit a b c d cej spisok zminnij notQuiteImmutableList getList add d Odin iz sposobiv obijti cyu problemu povernuti kopiyu masivu abo kolekciyi pri viklici gettera public List lt T gt getList povertaye kopiyu spisku tomu vnutrishnij stan pochatkovogo spisku ne mozhe buti zmineno return new ArrayList list JavaScript U JavaScript deyaki vbudovani tipi chisla ryadki nezminni ale koristuvalnicki ob yekti yak pravilo zminyuyutsya function doSomething x chi pominyaye h svij pervinnij stan var str a string var obj an object doSomething str ryadki chisla i logichni tipi nezminni funkciya otrimuye kopiyu doSomething obj ob yekti prohodyat za posilannyam i zminni vseredini funkciyi doAnotherThing str obj str ne pominyayetsya ale obj mozhe pominyatis Shob imituvati nezminnist v ob yekti vlastivosti mozhna viznachiti yak read only writable false var obj Object defineProperty obj foo value bar writable false obj foo bar2 ignoruyetsya Prote navedenij vishe pidhid dozvolyaye dodavati novi vlastivosti Krim togo mozhna vikoristovuvati Object freeze shob zrobiti isnuyuchi ob yekti nezminnimi var obj foo bar Object freeze obj obj foo bars ne mozhna zminiti vlastivist ignoruyetsya obj foo2 bar2 ne mozhna dodati vlastivist ignoruyetsya Z momentu vprovadzhennya React nezminnij stan vse chastishe vikoristovuyetsya v JavaScript i sho spriyaye poshirennya potoko podibnih modelej takih yak Redux PrimitkiGoetz et al Java Concurrency in Practice Addison Wesley Professional 2006 Section 3 4 Immutability Carlos Schults March 2018 C Immutable Types Understanding the Attraction ndepend com D Language Specification 18 How to create Immutable Class and Object in Java Tutorial Example Javarevisited blogspot co uk 4 bereznya 2013 Procitovano 14 kvitnya 2014 Neizmenyaemyj klass qaru site Immutability in JavaScript A Contrarian View Desalasworks Div takozhInvariant programuvannya Ce nezavershena stattya pro informacijni tehnologiyi Vi mozhete dopomogti proyektu vipravivshi abo dopisavshi yiyi