Միասնական պատասխանատվության սկզբունք. Ոչ այնքան պարզ, որքան թվում է

Միասնական պատասխանատվության սկզբունք. Ոչ այնքան պարզ, որքան թվում է Միասնական պատասխանատվության սկզբունք, որը նաև հայտնի է որպես միասնական պատասխանատվության սկզբունք,
aka սկզբունքը միատեսակ փոփոխականության - չափազանց սայթաքուն տղա է հասկանալ, եւ նման նյարդային հարց է ծրագրավորողի հարցազրույցի.

Այս սկզբունքի հետ իմ առաջին լուրջ ծանոթությունը տեղի ունեցավ առաջին կուրսի սկզբին, երբ ջահելներին ու կանաչներին տարան անտառ՝ թրթուրներից աշակերտներ սարքելու՝ իսկական ուսանողներ։

Անտառում բաժանվեցինք 8-9 հոգանոց խմբերի և անցկացրինք մրցույթ՝ ո՞ր խումբն է ամենաարագը մի շիշ օղի խմելու պայմանով, որ խմբի առաջինը օղին լցնի բաժակի մեջ, երկրորդը խմի։ իսկ երրորդը խորտիկ ունի: Գործողությունն ավարտած միավորը տեղափոխվում է խմբի հերթի վերջ:

Այն դեպքը, երբ հերթի չափը երեքից բազմապատիկ էր, SRP-ի լավ իրականացում էր:

Սահմանում 1. Մեկ պատասխանատվություն.

Միասնական պատասխանատվության սկզբունքի (SRP) պաշտոնական սահմանման մեջ նշվում է, որ յուրաքանչյուր սուբյեկտ ունի իր պատասխանատվությունը և գոյության պատճառները, և նա ունի միայն մեկ պատասխանատվություն:

Դիտարկենք «Խմող» առարկան (Թիփլեր).
SRP սկզբունքն իրականացնելու համար մենք պարտականությունները կբաժանենք երեքի.

  • Մեկը լցնում է (PourOperation)
  • Մեկը խմում է (DrinkUpOperation)
  • Մեկը խորտիկ ունի (TakeBiteOperation)

Գործընթացի մասնակիցներից յուրաքանչյուրը պատասխանատու է գործընթացի մեկ բաղադրիչի համար, այսինքն՝ ունի մեկ ատոմային պատասխանատվություն՝ խմել, լցնել կամ խորտիկ:

Խմելու փոսը, իր հերթին, հանդիսանում է ճակատ այս գործողությունների համար.

сlass Tippler {
    //...
    void Act(){
        _pourOperation.Do() // налить
        _drinkUpOperation.Do() // выпить
        _takeBiteOperation.Do() // закусить
    }
}

Միասնական պատասխանատվության սկզբունք. Ոչ այնքան պարզ, որքան թվում է

Ինչու?

Մարդ ծրագրավորողը կոդ է գրում կապիկ մարդու համար, իսկ կապիկ մարդը անուշադիր է, հիմար և միշտ շտապում է։ Նա կարող է միաժամանակ պահել և հասկանալ 3-7 տերմին:
Հարբածի դեպքում այս տերմիններից երեքն են. Սակայն եթե ծածկագիրը գրենք մեկ թերթիկով, ապա այն կպարունակի ձեռքեր, ակնոցներ, կռիվներ և անվերջ վեճեր քաղաքականության մասին։ Եվ այս ամենը կլինի մեկ մեթոդի մարմնում. Համոզված եմ, որ դուք տեսել եք նման օրենսգիրք ձեր պրակտիկայում: Հոգեկանի համար ամենամարդկային թեստը չէ։

Մյուս կողմից, կապիկ մարդը նախատեսված է իր գլխում իրական աշխարհի օբյեկտները նմանակելու համար: Իր երևակայության մեջ նա կարող է դրանք իրար մղել, դրանցից նոր առարկաներ հավաքել և նույն կերպ ապամոնտաժել։ Պատկերացրեք հին մոդելի մեքենա: Ձեր երևակայության մեջ դուք կարող եք բացել դուռը, արձակել դռան եզրագծերը և այնտեղ տեսնել պատուհանի վերելակի մեխանիզմները, որոնց ներսում կլինեն շարժակներ: Բայց դուք չեք կարող տեսնել մեքենայի բոլոր բաղադրիչները միաժամանակ՝ մեկ «ցուցակում»։ Գոնե «կապիկ մարդը» չի կարող։

Հետևաբար, մարդկային ծրագրավորողները բարդ մեխանիզմները տարրալուծում են ոչ այնքան բարդ և աշխատող տարրերի մի շարքի: Այնուամենայնիվ, այն կարող է քայքայվել տարբեր ձևերով. շատ հին մեքենաներում օդային խողովակը մտնում է դուռը, իսկ ժամանակակից մեքենաներում կողպեքի էլեկտրոնիկայի խափանումը խանգարում է շարժիչի գործարկմանը, ինչը կարող է խնդիր լինել վերանորոգման ժամանակ:

Այժմ, SRP-ն սկզբունք է, որը բացատրում է ԻՆՉՊԵՍ քայքայվել, այսինքն՝ որտեղ գծել բաժանարար գիծը.

Նա ասում է, որ անհրաժեշտ է քայքայվել «պատասխանատվության» բաժանման սկզբունքով, այսինքն՝ ըստ որոշակի օբյեկտների առաջադրանքների։

Միասնական պատասխանատվության սկզբունք. Ոչ այնքան պարզ, որքան թվում է

Վերադառնանք խմելուն և այն առավելություններին, որոնք ստանում է կապիկ մարդը տարրալուծման ժամանակ.

  • Կոդը չափազանց պարզ է դարձել բոլոր մակարդակներում
  • Կոդը կարող է գրվել միանգամից մի քանի ծրագրավորողների կողմից (յուրաքանչյուրը գրում է առանձին տարր)
  • Ավտոմատացված թեստավորումը պարզեցված է. որքան պարզ է տարրը, այնքան հեշտ է այն փորձարկել
  • Հայտնվում է կոդի կոմպոզիցիա. կարող եք փոխարինել DrinkUpOperation մի վիրահատության, որի ժամանակ հարբեցողը հեղուկ է լցնում սեղանի տակ։ Կամ լցնելու գործողությունը փոխարինեք մի գործողությամբ, որի ժամանակ խառնում եք գինին ու ջուրը կամ օղին ու գարեջուրը: Կախված բիզնեսի պահանջներից, դուք կարող եք ամեն ինչ անել առանց մեթոդի կոդը դիպչելու Tippler.Act.
  • Այս գործողություններից դուք կարող եք ծալել որկրամոլը (միայն օգտագործելով TakeBitOperation), ալկոհոլային (միայն օգտագործվող DrinkUpOperation ուղիղ շշից) և բավարարում է բազմաթիվ այլ բիզնես պահանջներ:

(Օ՜, կարծես սա արդեն OCP սկզբունք է, և ես խախտել եմ այս գրառման պատասխանատվությունը)

Եվ, իհարկե, թերությունները.

  • Մենք պետք է ավելի շատ տեսակներ ստեղծենք:
  • Հարբեցողն առաջին անգամ խմում է մի քանի ժամ ուշ, քան այլ կերպ կխմեր:

Սահմանում 2. Միասնական փոփոխականություն.

Թույլ տվեք, պարոնայք: Խմելու դասարանը նույնպես ունի մեկ պատասխանատվություն՝ խմում է: Իսկ ընդհանրապես, «պատասխանատվություն» բառը չափազանց անորոշ հասկացություն է։ Ինչ-որ մեկը պատասխանատու է մարդկության ճակատագրի համար, իսկ ինչ-որ մեկը պատասխանատու է բևեռում շրջված պինգվիններին մեծացնելու համար:

Դիտարկենք խմիչքի երկու իրականացում. Առաջինը, վերը նշվածը, պարունակում է երեք դաս՝ լցնել, խմել և խորտիկ։

Երկրորդը գրված է «Առաջ և միայն առաջ» մեթոդոլոգիայի միջոցով և պարունակում է մեթոդի ողջ տրամաբանությունը Վարմունք:

//Не тратьте время  на изучение этого класса. Лучше съешьте печеньку
сlass BrutTippler {
   //...
   void Act(){
        // наливаем
    if(!_hand.TryDischarge(from:_bottle, to:_glass, size:_glass.Capacity))
        throw new OverdrunkException();

    // выпиваем
    if(!_hand.TryDrink(from: _glass,  size: _glass.Capacity))
        throw new OverdrunkException();

    //Закусываем
    for(int i = 0; i< 3; i++){
        var food = _foodStore.TakeOrDefault();
        if(food==null)
            throw new FoodIsOverException();

        _hand.TryEat(food);
    }
   }
}

Այս երկու դասերն էլ, արտաքին դիտորդի տեսանկյունից, միանգամայն նույն տեսքն ունեն և կիսում են «խմելու» նույն պատասխանատվությունը։

Շփոթություն.

Այնուհետև մենք գնում ենք առցանց և պարզում ենք SRP-ի մեկ այլ սահմանում՝ «Single Changeability Principle»:

ՀՔԾ-ն նշում է, որ «Մոդուլը փոխելու մեկ և միայն մեկ պատճառ ունի«. Այսինքն՝ «Պատասխանատվությունը փոփոխությունների պատճառ է»։

(Թվում է, որ այն տղաները, ովքեր հորինել են սկզբնական սահմանումը, վստահ էին կապիկ մարդու տելեպատիկ ունակությունների վրա)

Հիմա ամեն ինչ իր տեղն է ընկնում։ Առանձին-առանձին մենք կարող ենք փոխել լցնելու, խմելու և խորտիկի ընթացակարգերը, բայց հենց խմիչում մենք կարող ենք փոխել միայն գործողությունների հաջորդականությունն ու բաղադրությունը, օրինակ՝ խմելուց առաջ խորտիկը տեղափոխելով կամ ավելացնելով կենացը:

«Առաջ և միայն առաջ» մոտեցման մեջ այն ամենը, ինչ կարելի է փոխել, փոխվում է միայն մեթոդով Վարմունք. Սա կարող է ընթեռնելի և արդյունավետ լինել, երբ տրամաբանությունը քիչ է և այն հազվադեպ է փոխվում, բայց հաճախ այն ավարտվում է սարսափելի մեթոդներով՝ յուրաքանչյուրը 500 տողով, ավելի շատ եթե-հայտարարություններով, քան պահանջվում է Ռուսաստանի համար ՆԱՏՕ-ին միանալու համար:

Սահմանում 3. Փոփոխությունների տեղայնացում.

Խմողները հաճախ չեն հասկանում, թե ինչու են արթնացել ուրիշի բնակարանում կամ որտեղ է իրենց բջջային հեռախոսը։ Ժամանակն է ավելացնել մանրամասն գրանցում:

Եկեք սկսենք մուտք գործել հորդառատ գործընթացով.

class PourOperation: IOperation{
    PourOperation(ILogger log /*....*/){/*...*/}
    //...
    void Do(){
        _log.Log($"Before pour with {_hand} and {_bottle}");
        //Pour business logic ...
        _log.Log($"After pour with {_hand} and {_bottle}");
    }
}

Ներկելով այն PourOperation, մենք խելամիտ գործեցինք պատասխանատվության և պարփակման տեսակետից, բայց հիմա մեզ շփոթել են փոփոխականության սկզբունքի հետ։ Բացի բուն գործողությունից, որը կարող է փոխվել, փոփոխական է դառնում նաև հատումները: Դուք պետք է առանձնացնեք և ստեղծեք հատուկ լոգեր հորդառատ գործողության համար.

interface IPourLogger{
    void LogBefore(IHand, IBottle){}
    void LogAfter(IHand, IBottle){}
    void OnError(IHand, IBottle, Exception){}
}

class PourOperation: IOperation{
    PourOperation(IPourLogger log /*....*/){/*...*/}
    //...
    void Do(){
        _log.LogBefore(_hand, _bottle);
        try{
             //... business logic
             _log.LogAfter(_hand, _bottle");
        }
        catch(exception e){
            _log.OnError(_hand, _bottle, e)
        }
    }
}

Բծախնդիր ընթերցողը դա կնկատի LogAfter, Մուտք Նախքան и OneError կարող է նաև փոփոխվել անհատապես և, նախորդ քայլերի համեմատությամբ, կստեղծի երեք դաս. PourLogger Նախքան, PourLogger Հետո и PourErrorLogger.

Եվ հիշելով, որ խմողի համար երեք վիրահատություն կա, մենք ստանում ենք անտառահատման ինը դաս: Արդյունքում խմելու ամբողջ շրջանակը բաղկացած է 14 (!!!) դասերից։

Հիպերբոլա? Դժվար թե՛։ Քայքայման նռնակով կապիկ մարդը «լցողին» կբաժանի ջրատարի, բաժակի, հորդառատ օպերատորների, ջրամատակարարման ծառայության, մոլեկուլների բախման ֆիզիկական մոդելի, և հաջորդ եռամսյակում նա կփորձի լուծարել կախվածությունը առանց գլոբալ փոփոխականներ. Եվ հավատացեք ինձ, նա կանգ չի առնի:

Հենց այս պահին շատերը գալիս են այն եզրակացության, որ ՊԵԿ-ը վարդագույն թագավորությունների հեքիաթներ են, և գնում են լապշա խաղալու...

... առանց երբևէ իմանալու Srp-ի երրորդ սահմանման գոյության մասին.

«Միասնական պատասխանատվության սկզբունքն ասում է, որ բաները, որոնք նման են փոփոխությանը, պետք է պահվեն մեկ տեղում«. կամ "Ինչ փոփոխությունները միասին պետք է պահվեն մեկ տեղում"

Այսինքն, եթե մենք փոխում ենք գործողության գրանցումը, ապա պետք է այն փոխենք մեկ տեղում։

Սա շատ կարևոր կետ է, քանի որ SRP-ի բոլոր բացատրությունները, որոնք վերը նշված էին, ասում էին, որ պետք է ջախջախել տեսակները, երբ դրանք ջախջախվում են, այսինքն, նրանք դրել են «վերին սահման» օբյեկտի չափի վրա, և այժմ. մենք արդեն խոսում ենք «ստորին սահմանի» մասին։ Այլ կերպ ասած, SRP-ն ոչ միայն պահանջում է «ջախջախել ջախջախելիս», այլև չչափել այն.. Սա մեծ ճակատամարտ է Օքամի ածելիի և կապիկ մարդու միջև:

Միասնական պատասխանատվության սկզբունք. Ոչ այնքան պարզ, որքան թվում է

Հիմա խմողը պետք է իրեն լավ զգա։ Բացի այն, որ կարիք չկա IPourLogger լոգերը բաժանել երեք դասի, մենք կարող ենք նաև միավորել բոլոր լոգերը մեկ տեսակի մեջ.

class OperationLogger{
    public OperationLogger(string operationName){/*..*/}
    public void LogBefore(object[] args){/*...*/}       
    public void LogAfter(object[] args){/*..*/}
    public void LogError(object[] args, exception e){/*..*/}
}

Իսկ եթե ավելացնենք չորրորդ տիպի գործողությունը, ապա դրա համար գրանցումն արդեն պատրաստ է։ Իսկ բուն գործառնությունների ծածկագիրը մաքուր է և զերծ ենթակառուցվածքային աղմուկից:

Արդյունքում մենք ունենք խմելու խնդրի լուծման 5 դաս.

  • Հորդառատ գործողություն
  • Խմելու գործողություն
  • Խցանման գործողություն
  • անտառահատ
  • Խմիչքի ճակատը

Նրանցից յուրաքանչյուրը խստորեն պատասխանատու է մեկ ֆունկցիոնալության համար և ունի փոփոխության մեկ պատճառ։ Փոփոխությանը նման բոլոր կանոնները գտնվում են մոտակայքում:

Իրական կյանքի օրինակ

Մենք մի անգամ գրել ենք b2b հաճախորդի ավտոմատ գրանցման ծառայություն: Եվ նմանատիպ բովանդակության 200 տողերի համար հայտնվեց ԱՍՏԾՈ մեթոդ.

  • Գնացեք 1C և ստեղծեք հաշիվ
  • Այս հաշվի միջոցով անցեք վճարման մոդուլ և ստեղծեք այն այնտեղ
  • Ստուգեք, որ նման հաշիվ ունեցող հաշիվ չի ստեղծվել հիմնական սերվերում
  • Նոր հաշիվ բացել
  • Գրանցման արդյունքները ավելացրեք վճարման մոդուլում և 1c համարը գրանցման արդյունքների ծառայությանը
  • Այս աղյուսակում ավելացրեք հաշվի տվյալները
  • Ստեղծեք կետի համար այս հաճախորդի համար կետային ծառայության մեջ: Այս ծառայությանը փոխանցեք ձեր 1c հաշվի համարը:

Եվ այս ցուցակում կային ևս 10 բիզնես գործառնություններ՝ սարսափելի կապով: Հաշվի օբյեկտը գրեթե բոլորին էր պետք։ Զանգերի կեսին անհրաժեշտ է եղել կետի ID-ն և հաճախորդի անունը:

Մեկ ժամ վերամշակումից հետո մենք կարողացանք ենթակառուցվածքի կոդը և հաշվի հետ աշխատելու որոշ նրբերանգներ առանձնացնել առանձին մեթոդների/դասերի: «Աստծո» մեթոդը հեշտացրեց այն, բայց մնաց 100 տող կոդ, որոնք պարզապես չէին ուզում լուծարվել:

Միայն մի քանի օր անց պարզ դարձավ, որ այս «թեթև» մեթոդի էությունը բիզնեսի ալգորիթմն է։ Եվ որ տեխնիկական բնութագրերի սկզբնական նկարագրությունը բավականին բարդ էր։ Եվ հենց այս մեթոդը կտոր-կտոր անելու փորձն է, որ կխախտի ՊԵԿ-ը, և ոչ հակառակը։

Ֆորմալիզմ.

Ժամանակն է հանգիստ թողնել մեր հարբածին։ Չորացրեք ձեր արցունքները, մենք անպայման մի օր կվերադառնանք դրան: Այժմ եկեք պաշտոնականացնենք այս հոդվածից ստացված գիտելիքները:

Ֆորմալիզմ 1. SRP-ի սահմանում

  1. Առանձնացրեք տարրերը, որպեսզի նրանցից յուրաքանչյուրը պատասխանատու լինի մեկ բանի համար:
  2. Պատասխանատվությունը նշանակում է «փոխելու պատճառ»: Այսինքն՝ յուրաքանչյուր տարր փոփոխության միայն մեկ պատճառ ունի՝ բիզնես տրամաբանության առումով։
  3. Հնարավոր փոփոխություններ բիզնեսի տրամաբանության մեջ. պետք է տեղայնացնել. Սինխրոն փոփոխվող տարրերը պետք է մոտակայքում լինեն:

Ֆորմալիզմ 2. Անհրաժեշտ ինքնաթեստավորման չափանիշներ.

ՊԵԿ-ի կատարման համար բավարար չափորոշիչներ չեմ տեսել։ Բայց կան անհրաժեշտ պայմաններ.

1) Հարցրեք ինքներդ ձեզ, թե ինչ է անում այս դասը/մեթոդը/մոդուլը/ծառայությունը: դուք պետք է դրան պատասխանեք պարզ սահմանումով. ( Շնորհակալություն Բրայթորի )

բացատրություններ

Այնուամենայնիվ, երբեմն շատ դժվար է պարզ սահմանում գտնել

2) Սխալը շտկելը կամ նոր գործառույթ ավելացնելը ազդում է ֆայլերի/դասերի նվազագույն քանակի վրա: Իդեալում `մեկ:

բացատրություններ

Քանի որ պատասխանատվությունը (հատկանիշի կամ սխալի համար) պարփակված է մեկ ֆայլ/դասում, դուք հստակ գիտեք, թե որտեղ փնտրել և ինչ խմբագրել: Օրինակ՝ գրանցման գործողությունների արդյունքը փոխելու հատկանիշը կպահանջի փոխել միայն լոգերը: Կարիք չկա անցնելու մնացած ծածկագիրը:

Մեկ այլ օրինակ է UI-ի նոր հսկողության ավելացումը, որը նման է նախորդներին: Եթե ​​սա ստիպում է ձեզ ավելացնել 10 տարբեր միավորներ և 15 տարբեր փոխարկիչներ, ապա թվում է, թե դուք չափն անցնում եք:

3) Եթե մի քանի մշակողներ աշխատում են ձեր նախագծի տարբեր հատկանիշների վրա, ապա միաձուլման կոնֆլիկտի հավանականությունը, այսինքն՝ նույն ֆայլը/դասը միաժամանակ մի քանի մշակողների կողմից փոխվելու հավանականությունը նվազագույն է:

բացատրություններ

Եթե ​​«Օղի լցնել սեղանի տակ» նոր օպերացիան ավելացնելիս պետք է ազդել լոգերի վրա, խմելու և լցնելու վրա, ապա թվում է, թե պարտականությունները ծուռ են բաժանված։ Իհարկե, դա միշտ չէ, որ հնարավոր է, բայց պետք է փորձել նվազեցնել այս ցուցանիշը։

4) Երբ բիզնես տրամաբանության մասին հստակեցնող հարց են տալիս (ծրագրավորողից կամ մենեջերից), դուք խստորեն մտնում եք մեկ դաս/ֆայլ և տեղեկատվություն ստանում միայն այնտեղից:

բացատրություններ

Առանձնահատկությունները, կանոնները կամ ալգորիթմները գրված են կոմպակտ կերպով, յուրաքանչյուրը մեկ տեղում և ոչ թե դրոշներով սփռված կոդի տարածության վրա:

5) Անվանումը պարզ է.

բացատրություններ

Մեր դասը կամ մեթոդը պատասխանատու է մեկ բանի համար, և պատասխանատվությունն արտացոլվում է դրա անվան մեջ

AllManagersManagerService - ամենայն հավանականությամբ Աստծո դաս
LocalPayment - հավանաբար ոչ

Ֆորմալիզմ 3. Occam-առաջին զարգացման մեթոդաբանություն.

Դիզայնի սկզբում կապիկ մարդը չգիտի և չի զգում լուծվող խնդրի բոլոր նրբությունները և կարող է սխալվել: Դուք կարող եք սխալներ թույլ տալ տարբեր ձևերով.

  • Դարձրեք առարկաները չափազանց մեծ՝ միաձուլելով տարբեր պարտականություններ
  • Վերակազմավորում՝ մեկ պատասխանատվությունը բաժանելով տարբեր տեսակների
  • Սխալ սահմանել պատասխանատվության սահմանները

Կարևոր է հիշել կանոնը՝ «ավելի լավ է մեծ սխալ թույլ տալ» կամ «եթե վստահ չեք, մի բաժանեք այն»։ Եթե, օրինակ, ձեր դասը պարունակում է երկու պարտականություն, ապա այն դեռ հասկանալի է և կարող է բաժանվել երկու մասի՝ հաճախորդի կոդի նվազագույն փոփոխություններով: Ապակու բեկորներից ապակի հավաքելը սովորաբար ավելի դժվար է մի քանի ֆայլերի վրա տարածված համատեքստի և հաճախորդի կոդում անհրաժեշտ կախվածությունների բացակայության պատճառով:

Ժամանակն է այն անվանել օր

SRP-ի շրջանակը չի սահմանափակվում OOP-ով և SOLID-ով: Այն վերաբերում է մեթոդներին, գործառույթներին, դասերին, մոդուլներին, միկրոծառայությունների և ծառայություններին: Այն վերաբերում է ինչպես «figax-figax-and-prod» և «rocket-science» զարգացմանը՝ աշխարհն ամենուր մի փոքր ավելի լավը դարձնելով: Եթե ​​մտածեք դրա մասին, սա գրեթե բոլոր ինժեներական սկզբունքն է: Մեքենաշինությունը, կառավարման համակարգերը և, իրոք, բոլոր բարդ համակարգերը կառուցված են բաղադրիչներից, և «թերֆրագմենտացիան» դիզայներներին զրկում է ճկունությունից, «գերֆրագմենտացիան» դիզայներներին զրկում է արդյունավետությունից, իսկ սխալ սահմանները զրկում են նրանց բանականությունից և մտքի հանգստությունից:

Միասնական պատասխանատվության սկզբունք. Ոչ այնքան պարզ, որքան թվում է

SRP-ն բնության կողմից հորինված չէ և ճշգրիտ գիտության մաս չէ: Այն դուրս է գալիս մեր կենսաբանական և հոգեբանական սահմանափակումներից: Սա պարզապես կապիկի մարդու ուղեղի միջոցով բարդ համակարգեր կառավարելու և զարգացնելու միջոց է: Նա մեզ ասում է, թե ինչպես կարելի է քայքայել համակարգը: Բնօրինակ ձևակերպումը պահանջում էր բավականաչափ հեռատեսություն, բայց հուսով եմ, որ այս հոդվածը մաքրում է ծխածածկույթը:

Source: www.habr.com

Добавить комментарий