Ֆունկցիոնալ Powershell-ը դասերով օքսիմորոն չէ, դա երաշխավորում եմ

Հե՜յ Հաբր։ Ձեր ուշադրությանն եմ ներկայացնում հոդվածի թարգմանությունը «Ֆունկցիոնալ PowerShell դասերի հետ.
Խոստանում եմ, որ դա օքսիմորոն չէ»
Քրիստոֆեր Կուեխի կողմից։

Օբյեկտակենտրոն և ֆունկցիոնալ ծրագրավորման պարադիգմները կարող են թվալ, որ հակասում են միմյանց, բայց երկուսն էլ հավասարապես աջակցվում են Powershell-ում: Գրեթե բոլոր ծրագրավորման լեզուները՝ ֆունկցիոնալ կամ ոչ, ունեն ընդլայնված անուն-արժեքի կապման հնարավորություններ. Դասերը, ինչպես կառուցվածքները և գրառումները, ընդամենը մեկ մոտեցում են: Եթե ​​մենք սահմանափակենք դասերի օգտագործումը անունների և արժեքների կապակցմամբ և խուսափենք ծանր օբյեկտի վրա հիմնված ծրագրավորման հասկացություններից, ինչպիսիք են ժառանգությունը, պոլիմորֆիզմը կամ փոփոխականությունը, մենք կարող ենք օգտվել դրանց առավելություններից՝ չբարդացնելով մեր կոդը: Ավելին, ավելացնելով անփոփոխ տիպի փոխակերպման մեթոդներ, մենք կարող ենք հարստացնել մեր ֆունկցիոնալ կոդը Դասերով։

Կաստաների կախարդանքը

Կաստերը Powershell-ի ամենահզոր հատկանիշներից են: Երբ դուք արժեք եք տալիս, դուք ապավինում եք անուղղակի սկզբնավորման և վավերացման հնարավորություններին, որոնք շրջակա միջավայրն ավելացնում է ձեր հավելվածին: Օրինակ, պարզապես տողը [xml]-ում գցելը այն կանցնի վերլուծիչ կոդի միջով և կստեղծի ամբողջական xml ծառ: Նույն նպատակով մենք կարող ենք օգտագործել Classes-ը մեր կոդի մեջ:

Դերերում հեշթեյլներ

Եթե ​​չունեք կոնստրուկտոր, կարող եք շարունակել առանց դրա՝ հեշթեյլ ուղարկելով ձեր դասի տեսակին: Մի մոռացեք օգտագործել վավերացման ատրիբուտները՝ այս օրինաչափությունից լիարժեք օգտվելու համար: Միևնույն ժամանակ, մենք կարող ենք օգտագործել դասի տպագրված հատկությունները` ավելի խորը սկզբնավորման և վավերացման տրամաբանությունը գործարկելու համար:

class Cluster {
    [ValidatePattern("^[A-z]+$")]
    [string] $Service
    [ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
    [string] $FlightingRing
    [ValidateSet("EastUS", "WestUS", "NorthEurope")]
    [string] $Region
    [ValidateRange(0, 255)]
    [int] $Index
}

[Cluster]@{
    Service       = "MyService"
    FlightingRing = "PROD"
    Region        = "EastUS"
    Index         = 2
}

Բացի այդ, ձուլումը օգնում է մաքուր արդյունք ստանալ: Համեմատեք Format-Table-ին փոխանցված Cluster hashtable զանգվածի ելքը այն ամենի հետ, ինչ դուք կստանաք, եթե առաջին անգամ այս հեշթեյլները գցեք դասի մեջ: Դասի հատկությունները միշտ թվարկվում են այն հերթականությամբ, որով դրանք սահմանված են այնտեղ: Մի մոռացեք ավելացնել թաքնված հիմնաբառը բոլոր այն հատկություններից առաջ, որոնք դուք չեք ցանկանում, որ տեսանելի լինեն արդյունքներում:

Ֆունկցիոնալ Powershell-ը դասերով օքսիմորոն չէ, դա երաշխավորում եմ

Իմաստների կաղապար

Եթե ​​դուք ունեք մեկ արգումենտով կոնստրուկտոր, ձեր դասի տիպին արժեքը կփոխանցի ձեր կոնստրուկտորին, որտեղ կարող եք սկզբնավորել ձեր դասի օրինակը:

class Cluster {
    [ValidatePattern("^[A-z]+$")]
    [string] $Service
    [ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
    [string] $FlightingRing
    [ValidateSet("EastUS", "WestUS", "NorthEurope")]
    [string] $Region
    [ValidateRange(0, 255)]
    [int] $Index

    Cluster([string] $id) {
        $this.Service, $this.FlightingRing, $this.Region, $this.Index = $id -split "-"
    }
}

[Cluster]"MyService-PROD-EastUS-2"

Հեռարձակել տողին

Դուք կարող եք նաև անտեսել [string] ToString() դասի մեթոդը՝ օբյեկտի տողերի ներկայացման հիմքում ընկած տրամաբանությունը սահմանելու համար, օրինակ՝ օգտագործելով տողերի ինտերպոլացիա:

class Cluster {
    [ValidatePattern("^[A-z]+$")]
    [string] $Service
    [ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
    [string] $FlightingRing
    [ValidateSet("EastUS", "WestUS", "NorthEurope")]
    [string] $Region
    [ValidateRange(0, 255)]
    [int] $Index

    [string] ToString() {
        return $this.Service, $this.FlightingRing, $this.Region, $this.Index -join "-"
    }
}

$cluster = [Cluster]@{
    Service       = "MyService"
    FlightingRing = "PROD"
    Region        = "EastUS"
    Index         = 2
}

Write-Host "We just created a model for '$cluster'"

Նկարել սերիականացված դեպքեր

Cast թույլ է տալիս անվտանգ deserialization. Ստորև բերված օրինակները ձախողվելու են, եթե տվյալները չեն համապատասխանում Cluster-ի մեր բնութագրերին

# Валидация сериализованных данных

[Cluster]$cluster = Get-Content "./my-cluster.json" | ConvertFrom-Json
[Cluster[]]$clusters = Import-Csv "./my-clusters.csv"

Կաստեր ձեր ֆունկցիոնալ կոդը

Ֆունկցիոնալ ծրագրերը նախ սահմանում են տվյալների կառուցվածքները, այնուհետև ծրագիրը իրականացնում են որպես անփոփոխ տվյալների կառուցվածքների վրա փոխակերպումների հաջորդականություն: Չնայած հակասական տպավորությանը, դասերը իսկապես օգնում են ձեզ գրել ֆունկցիոնալ կոդ՝ շնորհիվ տիպերի փոխակերպման մեթոդների։

Արդյո՞ք Powershell-ը, որը ես գրում եմ, ֆունկցիոնալ է:

C#-ից կամ նմանատիպ ծագում ունեցող շատ մարդիկ գրում են Powershell, որը նման է C#-ին: Դրանով դուք հեռանում եք ֆունկցիոնալ ծրագրավորման հայեցակարգերից և, հավանաբար, կշահեք Powershell-ում օբյեկտի վրա հիմնված ծրագրավորման մեջ կամ ավելին իմանալու ֆունկցիոնալ ծրագրավորման մասին:

Եթե ​​դուք մեծապես հիմնվում եք խողովակաշարերի (|), Where-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object և այլնի միջոցով անփոփոխ տվյալների փոխակերպման վրա, դուք ավելի ֆունկցիոնալ ոճ ունեք, և դուք կշահեք Powershell-ի օգտագործումից: դասեր ֆունկցիոնալ ոճով:

Դասերի ֆունկցիոնալ օգտագործումը

Կաստերը, թեև նրանք օգտագործում են այլընտրանքային շարահյուսություն, պարզապես քարտեզագրում են երկու տիրույթների միջև: Խողովակաշարում դուք կարող եք քարտեզագրել արժեքների զանգված՝ օգտագործելով ForEach-Object:

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

# Пример комбинирования классов с конвейерами для separation of concerns в конвейерах

class Node {
    [ValidateLength(3, 7)]
    [string] $Name
    [ValidateSet("INT", "PPE", "PROD")]
    [string] $FlightingRing
    [ValidateSet("EastUS", "WestUS", "NorthEurope", "WestEurope")]
    [string] $Region
    Node([string] $Name) {
        $Name -match "([a-z]+)(INT|PPE|PROD)([a-z]+)"
        $_, $this.Service, $this.FlightingRing, $this.Region = $Matches
        $this.Name = $Name
    }
}

class Datum {
    [string] $Name
    [int] $Value
    [Node] $Computer
    [int] Severity() {
        $this.Name -match "[0-9]+$"
        return $Matches[0]
    }
}

Write-Host "Urgent Security Audit Issues:"
Import-Csv "./audit-results.csv" `
    | ForEach-Object {[Datum]$_} `
    | Where-Object Value -gt 0 `
    | Group-Object {$_.Severity()} `
    | Where-Object Name -lt 2 `
    | ForEach-Object Group `
    | ForEach-Object Computer `
    | Where-Object FlightingRing -eq "PROD" `
    | Sort-Object Name, Region -Unique

Փաթեթավորման դաս՝ կրկնակի օգտագործման համար

Ոչինչ այնքան լավ չէ, որքան թվում է

Ցավոք, դասերը չեն կարող արտահանվել մոդուլներով այնպես, ինչպես ֆունկցիաները կամ փոփոխականները; բայց կան որոշ հնարքներ. Ենթադրենք, ձեր դասերը սահմանված են ./my-classes.ps1 ֆայլում

  • Դուք կարող եք dotsource ֆայլեր դասերով. ./my-classes.ps1. Սա կգործարկի my-classes.ps1-ը ձեր ընթացիկ տիրույթում և կսահմանի բոլոր դասերը այնտեղ գտնվող ֆայլից:

  • Դուք կարող եք ստեղծել Powershell մոդուլ, որը արտահանում է ձեր բոլոր հարմարեցված API-ները (cmdlets) և սահմանում ScriptsToProcess = «./my-classes.ps1» փոփոխականը ձեր մոդուլի մանիֆեստում, նույն արդյունքով. ./my-classes.ps1 կգործարկվի ձեր միջավայրը:

Որ տարբերակն էլ ընտրեք, հիշեք, որ Powershell-ի տիպային համակարգը չի կարող լուծել տարբեր վայրերից բեռնված նույն անվանման տեսակները:
Նույնիսկ եթե դուք տարբեր վայրերից բեռնել եք նույն հատկություններով երկու նույնական դասեր, դուք վտանգի եք ենթարկվում խնդիրների:

Forwardանապարհը առաջ

Տիպի լուծման խնդիրներից խուսափելու լավագույն միջոցը ձեր դասերը երբեք չբացահայտելն է օգտատերերին: Օգտվողի կողմից դասի կողմից սահմանված տիպի ներմուծում ակնկալելու փոխարեն, ձեր մոդուլից արտահանեք գործառույթ, որը վերացնում է դասին ուղղակիորեն մուտք գործելու անհրաժեշտությունը: Cluster-ի համար մենք կարող ենք արտահանել New-Cluster ֆունկցիա, որը կաջակցի օգտագործողի համար հարմար պարամետրերի հավաքածուներին և կվերադարձնի կլաստեր:

class Cluster {
    [ValidatePattern("^[A-z]+$")]
    [string] $Service
    [ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
    [string] $FlightingRing
    [ValidateSet("EastUS", "WestUS", "NorthEurope")]
    [string] $Region
    [ValidateRange(0, 255)]
    [int] $Index
}

function New-Cluster {
    [OutputType([Cluster])]
    Param(
        [Parameter(Mandatory, ParameterSetName = "Id", Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string] $Id,
        [Parameter(Mandatory, ParameterSetName = "Components")]
        [string] $Service,
        [Parameter(Mandatory, ParameterSetName = "Components")]
        [string] $FlightingRing,
        [Parameter(Mandatory, ParameterSetName = "Components")]
        [string] $Region,
        [Parameter(Mandatory, ParameterSetName = "Components")]
        [int] $Index
    )

    if ($Id) {
        $Service, $FlightingRing, $Region, $Index = $Id -split "-"
    }

    [Cluster]@{
        Service       = $Service
        FlightingRing = $FlightingRing
        Region        = $Region
        Index         = $Index
    }
}

Export-ModuleMember New-Cluster

Էլ ի՞նչ կարդալ

Դասերի մասին
Պաշտպանական PowerShell
Ֆունկցիոնալ ծրագրավորում PowerShell-ում

Source: www.habr.com

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