Аввалан ҳақиқат, ё чаро система бояд дар асоси сохтори пойгоҳи додаҳо тарҳрезӣ шавад

Эй Ҳабр!

Мо таҳқиқи мавзӯъро идома медиҳем Java и баҳор, аз ҷумла дар сатҳи базаи маълумот. Имрӯз мо шуморо даъват менамоем, ки дар бораи он, ки чаро ҳангоми тарҳрезии замимаҳои калон сохтори пойгоҳи додаҳост, на рамзи Java, ки бояд аҳамияти ҳалкунанда дошта бошад, ин чӣ гуна анҷом дода мешавад ва кадом истисноҳо аз ин қоида вуҷуд доранд.

Дар ин мақолаи хеле дер, ман мефаҳмонам, ки чаро ман боварӣ дорам, ки қариб дар ҳама ҳолатҳо модели додаҳо дар барнома бояд "аз пойгоҳи додаҳо" тарҳрезӣ шавад, на аз "аз имкониятҳои Java" (ё ҳар забони муштарии шумо. кор бо). Бо назардошти равиши дуюм, шумо пас аз он ки лоиҳаи шумо ба воя мерасед, шумо худро барои роҳи тӯлонии дард ва ранҷу азоб мегузоред.

Мақола дар асоси он навишта шудааст як савол, дар бораи Stack Overflow дода шудааст.

Муҳокимаҳои ҷолиб дар бораи Reddit дар бахшҳо /r/java и /r/барномасозӣ.

Насли код

Чӣ қадар ҳайрон шудам, ки чунин як сегменти хурди корбарон вуҷуд доранд, ки бо jOOQ шинос шуда, аз он ки jOOQ барои фаъолият ба тавлиди коди сарчашма ба таври ҷиддӣ такя мекунад, хашмгин мешаванд. Ҳеҷ кас шуморо аз истифодаи jOOQ, ки ба шумо мувофиқ аст, бозмедорад ё шуморо маҷбур намекунад, ки тавлиди кодро истифода баред. Аммо роҳи пешфарз (тавре ки дар дастур тавсиф шудааст) бо jOOQ кор кардан дар он аст, ки шумо бо схемаи пойгоҳи додаҳо (қасри) оғоз карда, онро бо истифода аз генератори коди jOOQ муҳандисии баръакс кунед, то ба ин васила маҷмӯи синфҳоро, ки ҷадвалҳои шуморо намояндагӣ мекунанд, ба даст оред ва баъд навъи навишт. - дархостҳои бехатар ба ин ҷадвалҳо:

	for (Record2<String, String> record : DSL.using(configuration)
//   ^^^^^^^^^^^^^^^^^^^^^^^ Информация о типах выведена на 
//   основании сгенерированного кода, на который ссылается приведенное
// ниже условие SELECT 
 
       .select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)
//           vvvvv ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^ сгенерированные имена
       .from(ACTOR)
       .orderBy(1, 2)) {
    // ...
}

Рамз ба таври дастӣ берун аз маҷлис ё дастӣ дар ҳар як ҷамъомад тавлид мешавад. Масалан, чунин барқароршавӣ метавонад фавран пас аз он ба амал ояд Муҳоҷирати пойгоҳи додаҳои Flyway, ки онро низ дастӣ ё худкор кардан мумкин аст.

Насли коди манбаъ

Фалсафаҳо, афзалиятҳо ва нуқсонҳои мухталифе, ки бо ин равишҳо ба тавлиди код алоқаманданд - дастӣ ва автоматӣ мавҷуданд, ки ман дар ин мақола ба таври муфассал муҳокима намекунам. Аммо, дар маҷмӯъ, тамоми нуқтаи рамзи тавлидшуда дар он аст, ки он ба мо имкон медиҳад, ки дар Java он “ҳақиқат”-ро, ки мо онро муқаррарӣ қабул мекунем, дар дохили система ё берун аз он дубора тавлид кунем. Ба маъное, ин ҳамон чизест, ки компиляторҳо ҳангоми тавлиди байткод, коди мошин ё ягон шакли дигари коди ибтидоӣ мекунанд - мо новобаста аз сабабҳои мушаххас муаррифии "ҳақиқат"-и худро бо забони дигар мегирем.

Чунин генераторҳои код бисёранд. Барои намуна, XJC метавонад рамзи Java дар асоси файлҳои XSD ё WSDL тавлид кунад. Принсип ҳамеша як аст:

  • Баъзе ҳақиқат вуҷуд дорад (дохилӣ ё беруна) - масалан, мушаххасот, модели додаҳо ва ғайра.
  • Мо ба намояндагии маҳаллии ин ҳақиқат дар забони барномасозии худ ниёз дорем.

Илова бар ин, тақрибан ҳамеша тавсия дода мешавад, ки чунин намояндагӣ эҷод карда шавад, то ки зиёдатӣ пешгирӣ карда шавад.

Провайдерҳои навъи ва коркарди эзоҳҳо

Эзоҳ: равиши дигар, муосиртар ва мушаххас барои тавлиди код барои jOOQ ин истифодаи провайдерҳои тип мебошад, зеро онҳо дар F # амалӣ карда мешаванд. Дар ин ҳолат, код аз ҷониби компилятор, воқеан дар марҳилаи тартибдиҳӣ тавлид мешавад. Аслан, чунин код дар шакли сарчашма вуҷуд надорад. Java дорои асбобҳои шабеҳ аст, гарчанде он қадар шево нест - протсессори аннотатсионӣ, масалан, Lombok.

Ба маъное, дар ин ҷо ҳамон чизҳое рух медиҳанд, ки дар ҳолати аввал, ба истиснои:

  • Шумо рамзи тавлидшударо намебинед (шояд ин вазъият барои касе нафратовар бошад?)
  • Шумо бояд боварӣ ҳосил кунед, ки намудҳо таъмин карда мешаванд, яъне "ҳақиқӣ" бояд ҳамеша дастрас бошанд. Ин дар мисоли Ломбок, ки "ҳақиқат"-ро шарҳ медиҳад, осон аст. Ин бо моделҳои пойгоҳи додаҳо, ки аз пайвасти доимии зинда вобастаанд, каме мураккабтар аст.

Мушкилоти тавлиди код чист?

Илова ба саволи мураккаб дар бораи чӣ гуна беҳтар кардани тавлиди код - дастӣ ё худкор, мо бояд қайд кунем, ки одамоне ҳастанд, ки боварӣ доранд, ки тавлиди код умуман лозим нест. Далели ин нуктаи назар, ки ман бо он бештар дучор меоям, дар он аст, ки пас аз он сохтани қубури сохтмон душвор аст. Бале, дар ҳақиқат мушкил аст. Хароҷоти иловагии инфрасохтор ба вуҷуд меояд. Агар шумо танҳо бо як маҳсулоти муайян оғоз карда истода бошед (хоҳ он jOOQ, хоҳ JAXB, хоҳ Hibernate ва ғайра), ташкили муҳити истеҳсолӣ вақтро талаб мекунад, ки шумо мехоҳед омӯзиши худи API-ро сарф кунед, то аз он арзиш ба даст оред. .

Агар хароҷоти вобаста ба фаҳмиши сохтори генератор хеле баланд бошад, пас, воқеан, API дар корношоямии генератори код кори бад кардааст (ва баъдтар маълум мешавад, ки мутобиқсозии корбар дар он низ мураккаб аст). Истифодабарӣ бояд барои ҳама гуна API афзалияти баландтарин бошад. Аммо ин танҳо як далел бар зидди тавлиди код аст. Дар акси ҳол, навиштани тасвири маҳаллии ҳақиқати дохилӣ ё берунӣ комилан дастӣ аст.

Бисёриҳо мегӯянд, ки онҳо барои иҷрои ин ҳама вақт надоранд. Онҳо мӯҳлатҳои барои Super Маҳсулоти худ ба охир мерасанд. Рузе конвейерхои монтажкуниро ба тартиб меандозем, вакт меёбем. Ман ба онҳо ҷавоб медиҳам:

Аввалан ҳақиқат, ё чаро система бояд дар асоси сохтори пойгоҳи додаҳо тарҳрезӣ шавад
Original, Алан О'Рурк, тамошобинон

Аммо дар Hibernate/JPA навиштани рамзи Java хеле осон аст.

Дар ҳақиқат. Барои Hibernate ва корбарони он, ин ҳам баракат ва ҳам лаънат аст. Дар Hibernate шумо метавонед танҳо якчанд объектро нависед, ба монанди:

	@Entity
class Book {
  @Id
  int id;
  String title;
}

Ва қариб ҳама чиз омода аст. Ҳоло бояд "Тафсилот" -и мураккаберо тавлид кунад, ки ин объект дар DDL-и "лаҳҷаи" SQL-и шумо чӣ гуна муайян карда мешавад:

	CREATE TABLE book (
  id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
  title VARCHAR(50),
 
  CONSTRAINT pk_book PRIMARY KEY (id)
);
 
CREATE INDEX i_book_title ON book (title);

... ва иҷрои барномаро оғоз кунед. Имконияти воқеан олӣ барои зуд оғоз кардан ва кӯшиши чизҳои гуногун.

Бо вуҷуди ин, лутфан ба ман иҷозат диҳед. Ман дурӯғ мегуфтам.

  • Оё Hibernate воқеан таърифи ин калиди ибтидоии номбаршударо иҷро мекунад?
  • Оё Hibernate дар TITLE индекс эҷод мекунад? — Ман аниқ медонам, ки ба мо лозим меояд.
  • Оё Hibernate маҳз ин калидро дар мушаххасоти шахсият муайян мекунад?

Шояд не. Агар шумо лоиҳаи худро аз сифр таҳия карда истода бошед, пас аз илова кардани эзоҳҳои зарурӣ ҳамеша қулай аст, ки пойгоҳи додаҳои кӯҳнаро партофта, маълумоти нав эҷод кунед. Ҳамин тариқ, объекти Китоб дар ниҳоят чунин шакл мегирад:

	@Entity
@Table(name = "book", indexes = {
  @Index(name = "i_book_title", columnList = "title")
})
class Book {
  @Id
  @GeneratedValue(strategy = IDENTITY)
  int id;
  String title;
}

Хуб. Бозсозӣ. Боз ҳам, дар ин ҳолат дар оғоз хеле осон хоҳад буд.

Аммо шумо бояд баъдтар пардохт кунед

Дер ё зуд шумо бояд ба истехсолот равед. Он вақт ин модел аз кор мемонад. Зеро:

Дар истехсолот дигар, агар лозим бошад, базаи кухнаро партофта, аз сифр шуруъ кардан мумкин нест. Пойгоҳи базаи шумо мерос мешавад.

Минбаъд ва то абад шумо бояд навиштед Скриптҳои муҳоҷирати DDL, масалан, бо истифода аз Flyway. Дар ин ҳолат бо субъектҳои шумо чӣ мешавад? Шумо метавонед онҳоро ба таври дастӣ мутобиқ кунед (ва ба ин васила сарбории кори худро дучанд кунед) ё шумо метавонед ба Hibernate бигӯед, ки онҳоро барои шумо дубора тавлид кунад (то чӣ андоза онҳо бо ин роҳ ба интизориҳои шумо мувофиқат мекунанд?) Дар ҳар сурат, шумо аз даст медиҳед.

Пас, вақте ки шумо ба истеҳсолот ворид мешавед, ба шумо часбҳои гарм лозим мешавад. Ва онхоро хеле зуд дар истехсолот чорй кардан лозим аст. Азбаски шумо як лӯлаи ҳамвори муҳоҷирати худро барои истеҳсол омода накардаед ва ташкил накардаед, шумо ҳама чизро ба таври ваҳшӣ часпонед. Ва он гоҳ шумо дигар вақт надоред, ки ҳама чизро дуруст иҷро кунед. Ва шумо Hibernate-ро танқид мекунед, зеро ин ҳамеша айби каси дигар аст, на шумо...

Баръакс, корхоро аз худи аввал тамоман дигар хел кардан мумкин буд. Масалан, чархҳои мудавварро ба велосипед гузоред.

Аввалин пойгоҳи додаҳо

"Ҳақиқат" дар схемаи пойгоҳи додаҳои шумо ва "суверенитет" бар он дар дохили пойгоҳи додаҳо ҷойгир аст. Схема танҳо дар худи махзани маълумот муайян карда мешавад ва дар ҷои дигаре нест ва ҳар як муштарӣ нусхаи ин схемаро дорад, аз ин рӯ, риояи схема ва якпорчагии онро риоя кардан, дуруст иҷро кардани он дар базаи маълумот маънои комил дорад - дар он ҷо маълумот захира карда шудааст.
Ин хикмати кухна ва хатто хакконй аст. Калидҳои ибтидоӣ ва беназир хубанд. Калидҳои хориҷӣ хубанд. Санҷиши маҳдудиятҳо хуб аст. Тасдиқҳо - Хуб.

Гузашта аз ин, ин ҳама нест. Масалан, бо истифода аз Oracle, шумо эҳтимол мехоҳед муайян кунед:

  • Мизи шумо дар кадом фазои миз ҷойгир аст?
  • Арзиши PCTFREE он чист?
  • Андозаи кэш дар пайдарпаии шумо чист (дар паси ID)

Ин метавонад дар системаҳои хурд муҳим набошад, аммо ба шумо лозим нест, ки то гузаштан ба соҳаи бузурги додаҳо мунтазир шавед - шумо метавонед аз оптимизатсияи нигоҳдории аз ҷониби фурӯшанда таъминшуда, ба монанди онҳое, ки дар боло зикр шудаанд, зудтар оғоз кунед. Ҳеҷ яке аз ORM-ҳое, ки ман дидаам (аз ҷумла jOOQ) дастрасӣ ба маҷмӯи пурраи имконоти DDL-ро, ки шумо мехоҳед дар пойгоҳи додаи худ истифода баред, таъмин намекунад. ORMs баъзе асбобҳоро пешниҳод мекунанд, ки ба шумо дар навиштани DDL кӯмак мерасонанд.

Аммо дар охири рӯз як схемаи хуб тарҳрезӣшуда дар DDL дастӣ навишта мешавад. Ҳар як DDL тавлидшуда танҳо тахминии он аст.

Дар бораи модели муштарӣ чӣ гуфтан мумкин аст?

Тавре ки дар боло зикр гардид, дар муштарӣ ба шумо нусхаи схемаи пойгоҳи додаи худ, намуди муштарӣ лозим аст. Бояд қайд кард, ки ин намуди муштарӣ бояд бо модели воқеӣ ҳамоҳанг бошад. Роҳи беҳтарини расидан ба ин кадом аст? Истифодаи генератори код.

Ҳама пойгоҳи додаҳо маълумоти метаи худро тавассути SQL таъмин мекунанд. Ин аст, ки чӣ тавр ҳамаи ҷадвалҳоро аз базаи худ бо лаҳҷаҳои гуногуни SQL гирифтан мумкин аст:

	-- H2, HSQLDB, MySQL, PostgreSQL, SQL Server
SELECT table_schema, table_name
FROM information_schema.tables
 
-- DB2
SELECT tabschema, tabname
FROM syscat.tables
 
-- Oracle
SELECT owner, table_name
FROM all_tables
 
-- SQLite
SELECT name
FROM sqlite_master
 
-- Teradata
SELECT databasename, tablename
FROM dbc.tables

Ин дархостҳо (ё саволҳои шабеҳ, вобаста ба он, ки оё шумо бояд дидгоҳҳо, манзараҳои моддӣ, функсияҳои арзишноки ҷадвалро баррасӣ кунед) инчунин тавассути занг иҷро карда мешаванд. DatabaseMetaData.getTables() аз JDBC, ё бо истифода аз jOOQ мета-модули.

Аз натиҷаҳои чунин дархостҳо, новобаста аз он, ки шумо кадом технологияро дар муштарӣ истифода мебаред, тавлид кардани ҳама гуна намояндагии муштарии модели пойгоҳи додаҳо нисбатан осон аст.

  • Агар шумо JDBC ё Spring -ро истифода баред, шумо метавонед маҷмӯи доимии сатрро эҷод кунед
  • Агар шумо JPA-ро истифода баред, шумо метавонед худи объектҳоро эҷод кунед
  • Агар шумо jOOQ-ро истифода баред, шумо метавонед мета-модели jOOQ-ро тавлид кунед

Вобаста аз он, ки API-и муштарии шумо чӣ қадар функсияҳоро пешниҳод мекунад (масалан, jOOQ ё JPA), модели мета тавлидшуда метавонад воқеан бой ва мукаммал бошад. Масалан, имкони пайвастшавии номуайянро гиред, дар jOOQ 3.11 ҷорӣ карда шудааст, ки ба маълумоти мета тавлидшуда дар бораи муносибатҳои калидии хориҷӣ, ки дар байни ҷадвалҳои шумо мавҷуданд, такя мекунад.

Акнун ҳама гуна афзоиши пойгоҳи додаҳо рамзи муштариро ба таври худкор навсозӣ мекунад. Масалан, тасаввур кунед:

ALTER TABLE book RENAME COLUMN title TO book_title;

Оё шумо дар ҳақиқат мехоҳед, ки ин корро ду маротиба иҷро кунед? Дар ҳеҷ сурат. Танҳо DDL-ро иҷро кунед, онро тавассути лӯлаи сохтани худ иҷро кунед ва объекти навшударо гиред:

@Entity
@Table(name = "book", indexes = {
 
  // Вы об этом задумывались?
  @Index(name = "i_book_title", columnList = "book_title")
})
class Book {
  @Id
  @GeneratedValue(strategy = IDENTITY)
  int id;
 
  @Column("book_title")
  String bookTitle;
}

Ё синфи навшудаи jOOQ. Аксарияти тағйироти DDL на танҳо ба синтаксис, балки ба семантика низ таъсир мерасонанд. Аз ин рӯ, барои дидани коди тартибдодашуда дидан муфид аст, то бубинед, ки афзоиши пойгоҳи додаи шумо ба кадом код таъсир мерасонад (ё метавонад).

Ягона ҳақиқат

Новобаста аз он ки шумо кадом технологияро истифода мебаред, ҳамеша як модел вуҷуд дорад, ки барои баъзе зерсистема манбаи ягонаи ҳақиқат аст - ё, ҳадди аққал, мо бояд барои ин кӯшиш кунем ва аз чунин нофаҳмиҳои корхона канорагирӣ кунем, ки "ҳақиқат" дар ҳама ҷо ва дар ҳеҷ ҷо якбора нест. . Ҳама чиз метавонад хеле соддатар бошад. Агар шумо танҳо файлҳои XML-ро бо ягон системаи дигар мубодила кунед, танҳо XSD-ро истифода баред. Ба мета-модели INFORMATION_SCHEMA аз jOOQ дар шакли XML нигаред:
https://www.jooq.org/xsd/jooq-meta-3.10.0.xsd

  • XSD хуб фаҳмида мешавад
  • XSD мундариҷаи XML-ро хеле хуб нишон медиҳад ва имкон медиҳад, ки дар ҳама забонҳои муштарӣ тасдиқ карда шавад
  • XSD версияи хуб дорад ва мутобиқати пешрафтаи аќиб дорад
  • XSD-ро бо истифода аз XJC ба коди Java тарҷума кардан мумкин аст

Нуқтаи охирин муҳим аст. Ҳангоми муошират бо системаи беруна бо истифода аз паёмҳои XML, мо мехоҳем боварӣ ҳосил кунем, ки паёмҳои мо дурустанд. Ин бо истифода аз JAXB, XJC ва XSD хеле осон аст. Чунин фикр кардан, ки бо усули тарҳрезии "Java аввал", ки мо паёмҳои худро ҳамчун объектҳои Java месозем, комилан девонаворӣ мебуд, ки онҳо метавонанд ба таври мувофиқ ба XML харита карда шаванд ва ба системаи дигар барои истеъмол фиристода шаванд. XML, ки бо ин роҳ тавлид мешавад, сифати хеле паст, ҳуҷҷатгузорӣ ва таҳияи мушкил хоҳад буд. Агар барои чунин интерфейс шартномаи сатҳи хидматрасонӣ (SLA) мавҷуд бошад, мо фавран онро вайрон мекардем.

Рости гап, ин ҳама вақт бо API-ҳои JSON рӯй медиҳад, аммо ин як ҳикояи дигар аст, ман дафъаи оянда ҷанҷол мекунам ...

Пойгоҳи додаҳо: онҳо як чизанд

Ҳангоми кор бо пойгоҳи додаҳо, шумо мефаҳмед, ки ҳамаи онҳо асосан ба ҳам монанданд. Пойгоҳ маълумоти худро дорад ва бояд нақшаро идора кунад. Ҳама тағиротҳое, ки ба схема ворид карда шудаанд, бояд бевосита дар DDL амалӣ карда шаванд, то манбаи ягонаи ҳақиқат нав карда шавад.

Вақте ки навсозии манбаъ рух дод, ҳамаи муштариён бояд нусхаҳои модели худро навсозӣ кунанд. Баъзе муштариён метавонанд дар Java бо истифода аз jOOQ ва Hibernate ё JDBC (ё ҳарду) навишта шаванд. Мизоҷони дигарро бо Perl навиштан мумкин аст (мо ба онҳо танҳо барори кор мехоҳем), дар ҳоле ки дигаронро бо C# навиштан мумкин аст. Фарқ надорад. Модели асосӣ дар базаи маълумот аст. Моделҳое, ки бо истифода аз ORM тавлид мешаванд, одатан сифати паст, ҳуҷҷатгузории суст ва таҳияи мушкил мебошанд.

Пас хато накунед. Аз аввал хато накунед. Аз базаи маълумот кор кунед. Сохтани қубури ҷойгиркунӣ, ки метавонад автоматӣ карда шавад. Генераторҳои кодро фаъол созед, то нусхабардории модели пойгоҳи додаи шуморо осон кунад ва онро ба мизоҷон партояд. Ва ташвишро дар бораи генераторҳои код бас кунед. Онҳо хубанд. Бо онҳо шумо самараноктар хоҳед шуд. Шумо танҳо лозим аст, ки вақти каме барои насб кардани онҳо аз аввал сарф кунед - ва он гоҳ солҳои зиёд ҳосилнокӣ шуморо интизоранд, ки таърихи лоиҳаи шуморо ташкил медиҳад.

Ба ман ташаккур нагӯед, баъдтар.

Шарҳ

Барои равшан будан: Ин мақола ба ҳеҷ ваҷҳ тарафдорӣ намекунад, ки шумо бояд тамоми системаро (яъне, домен, мантиқи тиҷорат ва ғайра) хам кунед, то ба модели пойгоҳи додаатон мувофиқат кунед. Он чизе ки ман дар ин мақола мегӯям, ин аст, ки рамзи муштарӣ, ки бо пойгоҳи додаҳо ҳамкорӣ мекунад, бояд дар асоси модели пойгоҳи додаҳо амал кунад, то худи он модели пойгоҳи додаҳоро дар ҳолати "дараҷаи аввал" дубора тавлид накунад. Ин мантиқ одатан дар қабати дастрасии маълумот дар муштарии шумо ҷойгир аст.

Дар меъмории дудараҷа, ки то ҳол дар баъзе ҷойҳо нигоҳ дошта мешаванд, чунин модели система метавонад ягона модели имконпазир бошад. Аммо, дар аксари системаҳо қабати дастрасии додаҳо ба назарам як "зерсистема" аст, ки модели пойгоҳи додаҳоро фаро мегирад.

Бештар

Ҳар як қоида истисноҳо вуҷуд доранд ва ман аллакай гуфта будам, ки равиши тавлиди пойгоҳи додаҳо ва коди манбаъ баъзан метавонад номувофиқ бошад. Инҳоянд чанде аз чунин истисноҳо (эҳтимолан дигарон ҳастанд):

  • Вақте ки схема номаълум аст ва бояд кашф карда шавад. Масалан, шумо провайдери асбобе ҳастед, ки ба корбарон дар паймоиши ҳама гуна диаграмма кӯмак мекунад. Уф. Дар ин ҷо тавлиди код вуҷуд надорад. Аммо ба ҳар ҳол, базаи маълумот дар ҷои аввал аст.
  • Вақте ки барои ҳалли баъзе мушкилот як схема бояд дар парвоз тавлид карда шавад. Ин мисол ба як варианти каме хаёлии намуна ба назар мерасад арзиши атрибути объект, яъне, шумо дар ҳақиқат нақшаи аниқ муайян надоред. Дар ин ҳолат, шумо аксар вақт ҳатто боварӣ надоред, ки RDBMS ба шумо мувофиқ аст.

Истисноҳо табиатан истисноӣ мебошанд. Дар аксари ҳолатҳое, ки бо истифодаи RDBMS алоқаманд аст, схема пешакӣ маълум аст, он дар дохили RDBMS ҷойгир аст ва ягона манбаи "ҳақиқат" аст ва ҳама муштариён бояд нусхаҳои аз он гирифташударо ба даст оранд. Идеалӣ, шумо бояд генератори кодро истифода баред.

Манбаъ: will.com

Илова Эзоҳ