Powershell funksional me klasa — nuk është oksimoron, e garantoj këtë

Përshëndetje, Habr! Po ju prezantoj një përkthim të artikullit "Powershell funksional me Klasa."
Të premtoj se nuk është një oksimoron"
autori Christopher Kuech.

Paradigmat objektiv-orientuar dhe funksional të programeve mund të duken si në kundërshtim me njëra-tjetrën, por të dyja janë mbështetur njëlloj në Powershell. Praktikisht të gjitha gjuhët programeve, funksionale dhe jo, kanë mjete për lidhjen e avancuar të emrave dhe vlerave; Klasat, ashtu si struct-të dhe record-të, janë vetëm një qasje. Nëse e kufizojmë përdorimin e Klasa në lidhjen e emrave dhe vlerave dhe shmangim konceptet e rënda të programimit objektiv, si trashëgimi, polimorfizëm, ose ndryshueshmëri (mutability), mund të shfrytëzojmë përfitimet e tyre pa e komplikuar kodin tonë. Duke shtuar gjithashtu metoda të pa ndryshueshme (immutable) për konvertimin e tipeve, mund ta pasurojmë kodin tonë funksional me Klasa.

Magjia e kastit

Kastet janë një nga funksionet më të fuqishme në Powershell. Kur i nënshtroheni një vlerë kastit, mbështeteni në mundësinë për fillimin dhe validimin implicit të shtuar nga mjedisi në aplikacionin tuaj. Për shembull, një kast i thjeshtë i një vargu në [xml] do ta kalojë atë përmes kodit analizues dhe do të gjenerojë një pemë të plotë xml. Ne mund të përdorim Klasa në kodin tonë me të njëjtin qëllim.

Kasti i heshutabllave

Nëse nuk keni një ndërtues, mund të vazhdoni pa të, duke përdorur kast heshutabllat në tipin e klasës suaj. Mos harroni të shfrytëzoni atributet e validimit për të shfrytëzuar plotësisht këtë model. Po ashtu, mund të përdorim pronat e tipizuara të klasës për të aktivizuar logjikën e thellë të fillimit dhe validimit.

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
}

Për më tepër, kasti ndihmon për të marrë një dalje të pastër. Krahasoni daljen e një vargu heshutabllash Cluster të kaluar në Format-Table me atë që do të merrni nëse më parë kastoni këto heshutablla në një klasë. Pronat e klasës renditen gjithmonë në rendin në të cilin janë të përcaktuara. Mos harroni të shtoni fjalën kyçe hidden para të gjitha pronave që nuk duhet të jenë të dukshme në dalje.

Powershell funksional me klasa — nuk është oksimoron, e garantoj këtë

Kasti i vlerave

Nëse keni një konstruktor me një argument, hedhja e vlerës në tipin tuaj të klasës do të kalojë vlerën në këtë konstrukt, ku mund të inicializoni instancën e klasës tuaj.

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"

Hedhja në varg

Gjithashtu, mund të tejkaloni metodën e klasës [string] ToString() për të përcaktuar logjikën e paraqitjes string të objektit, për shembull, duke përdorur interpolimin e vargjeve.

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 "Kemi krijuar një model për '$cluster'"

Hedhje e instancave të serializuara

Hedhja lejon deserializim të sigurt. Shembujt më poshtë do të përfundojnë me gabimin, nëse të dhënat nuk përputhen me specifikimin tonë në Cluster.

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

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

Hedhjet në kodin tuaj funksional

Programet funksionale fillojnë me përcaktimin e strukturave të të dhënave, dhe pastaj implementojnë programin si një sekuencë transformacionesh mbi struktura të pafryshme të të dhënave. Pavarësisht nga përshtypja kontradiktore, klasat vërtet ndihmojnë në shkrimin e kodit funksional, falë metodave të konvertimit të tipeve.

A po shkruaj një Powershell funksional?

Shumë njerëz që vijnë nga C# ose kanë një histori të ngjashme shkruajnë Powershell që i ngjan C#. Duke bërë kështu, refuzoni të përdorni konceptet e programimit funksional dhe, ndoshta, do të përfitoni më shumë duke u zhytur thellë në programimin e orientuar në objekte në Powershell ose duke studiuar më mirë programimin funksional.

Nëse mbështeteni thellë në transformimin e të dhënave të pandryshueshme, duke përdorur tubat (|), Where-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object etj. — keni një stil më funksional dhe do t'ju ndihmojë përdorimi i klasave të Powershell në stil funksional.

Përdorimi funksional i klasave

Kastat, megjithëse përdorin një sintaksë alternative, janë thjesht një mapim midis dy domainesh. Në fushën e punës, mund të maponi një array vlerash duke përdorur ForEach-Object.

Në shembullin më poshtë, konstruktori Node ekzekutohet çdo herë kur ndodh një kast në Datum, dhe kjo na jep mundësinë të mos shkruajmë një përmasë të madhe kodi. Si rezultat, fushë puna jonë fokusohet në kërkesat deklarative të të dhënave dhe agregimin, ndërsa klasat tona marrin përsipër analizimin e të dhënave dhe validimin.

# Пример комбинирования классов с конвейерами для 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

Paketimi i klasës për ripërdorim

Asgjë nuk është aq e mirë sa duket

Fatkeqësisht, klasat nuk mund të eksportohen nga modulet njësoj si funksionet ose variablat; por ka disa truqe. Supozoni se klasat tuaja janë të përcaktuara në skedarin .\/my-classes.ps1

  • Mund të bëni një dot-sourcing të skedarit me klasat: . .\/my-classes.ps1. Kjo do të ekzekutojë my-classes.ps1 në hapësirën tuaj aktuale dhe do të përcaktojë të gjitha klasat nga skedari.

  • Mund të krijoni një modul Powershell, i cili eksporton të gjitha API-të tuaja të personalizuara (cmdlet) dhe të vendosni variablën ScriptsToProcess = ".\/my-classes.ps1" në manifestin e modulit tuaj, me të njëjtin rezultat: .\/my-classes.ps1 do të ekzekutohet në ambientin tuaj.

Pavarësisht nga varianti që zgjidhni, mos harroni se sistemi i tipeve Powershell nuk mund të zgjidhë tipe me emra të njëjtë, të ngarkuara nga vende të ndryshme.
Edhe nëse keni ngarkuar nga vende të ndryshme dy klasa identike me të njëjtat prona, rrezikoni të hasni probleme.

Rruga përpara

Mënyra më e mirë për të shmangur probleme me zgjidhjen e tipeve është kurrë të mos i ekspozoni klasat tuaja për përdoruesit. Në vend që të prisni që përdoruesi të importojë një tip të përcaktuar në klasë, eksportoni nga moduli juaj një funksion, i cili e çliron përdoruesin nga nevoja për t'u drejtuar drejtpërdrejt në klasë. Sa i përket Cluster, ne mund të eksportojmë funksionin New-Cluster, i cili do të mbështesë grupe miqësore për përdoruesin të parametrave dhe do të kthejë 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

Çfarë të lexoni tjetër

Rreth Klasave
Programimi Funksional në PowerShell
Powershell Funksional me Klasat — nuk është oksimoron, e garantoj këtë | ProHoster

Burimi: habr.com

Blini hosting të besueshëm për faqe interneti me mbrojtje nga DDoS, serverë VPS VDS 🔥 Blini hosting të besueshëm për faqe interneti me mbrojtje nga DDoS, serverë VPS VDS | ProHoster