Funksjonele Powershell mei klassen is gjin oxymoron, ik garandearje it

Hoi Habr! Ik presintearje jo oandacht de oersetting fan it artikel "Funksjonele PowerShell mei klassen.
Ik beloof dat it gjin oxymoron is"
troch Christopher Kuech.

Objekt-rjochte en funksjonele programmearring paradigma's kinne lykje yn striid mei elkoar, mar beide wurde likegoed stipe yn Powershell. Hast alle programmearring talen, funksjoneel of net, hawwe foarsjennings foar útwreide namme-wearde bining; Klassen, lykas struktueren en records, binne mar ien oanpak. As wy ús gebrûk fan Klassen beheine ta it binen fan nammen en wearden, en swiere objektrjochte programmearringskonsepten foarkomme lykas erfskip, polymorfisme of mutabiliteit, kinne wy ​​​​foardielen fan har foardielen nimme sûnder ús koade te komplisearjen. Fierder, troch it tafoegjen fan ûnferoarlike metoaden foar konverzje fan type, kinne wy ​​​​ús funksjonele koade ferrykje mei Klassen.

De magy fan kasten

Kasten binne ien fan 'e machtichste funksjes yn Powershell. As jo ​​​​in wearde castje, fertrouwe jo op 'e ymplisite inisjalisaasje- en falidaasjemooglikheden dy't de omjouwing tafoegje oan jo applikaasje. Bygelyks, gewoan in tekenrige yn [xml] castje sil it troch de parserkoade útfiere en in folsleine xml-beam generearje. Wy kinne Klassen brûke yn ús koade foar itselde doel.

Cast hashtabellen

As jo ​​​​gjin konstruktor hawwe, kinne jo trochgean sûnder ien troch in hashtabel te casten nei jo klassetype. Ferjit net de falidaasjeattributen te brûken om dit patroan folslein te profitearjen. Tagelyk kinne wy ​​​​de typearre eigenskippen fan 'e klasse brûke om noch djipper inisjalisaasje- en falidaasjelogika út te fieren.

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
}

Derneist helpt casting om in skjinne útfier te krijen. Fergelykje de útfier fan 'e Cluster-hashtable-array trochjûn oan Format-Table mei wat jo krije as jo dizze hashtabellen earst yn in klasse castje. De eigenskippen fan in klasse wurde altyd neamd yn 'e folchoarder wêryn't se dêr definiearre binne. Ferjit net it ferburgen kaaiwurd ta te foegjen foar al dy eigenskippen dy't jo net sichtber wolle yn 'e resultaten.

Funksjonele Powershell mei klassen is gjin oxymoron, ik garandearje it

Cast fan betsjuttingen

As jo ​​​​in konstruktor hawwe mei ien argumint, sil it castjen fan in wearde nei jo klassetype de wearde trochjaan oan jo constructor, wêr't jo in eksimplaar fan jo klasse kinne inisjalisearje

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"

Giet nei line

Jo kinne ek de [string] ToString ()-klassemetoade oerskriuwe om de logika efter de string-representaasje fan it objekt te definiearjen, lykas it brûken fan string-ynterpolaasje.

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 serialisearre eksimplaren

Cast kinne feilige deserialization. De foarbylden hjirûnder sille mislearje as de gegevens net foldogge oan ús spesifikaasje yn Cluster

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

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

Kasten yn jo funksjonele koade

Funksjonele programma's definiearje earst gegevensstruktueren, implementearje it programma dan as in folchoarder fan transformaasjes oer ûnferoarlike gegevensstruktueren. Nettsjinsteande de tsjinstridige yndruk, klassen helpe jo wirklik funksjonele koade te skriuwen troch metoaden foar typekonverzje.

Is de Powershell dy't ik skriuw funksjoneel?

In protte minsken dy't komme út C # of ferlykbere eftergrûnen skriuwe Powershell, dat is gelyk oan C #. Troch dit te dwaan, geane jo fuort fan it brûken fan funksjonele programmearringskonsepten en soene jo wierskynlik profitearje fan it dûke swier yn objekt-rjochte programmearring yn Powershell of mear leare oer funksjonele programmearring.

As jo ​​sterk fertrouwe op it transformearjen fan ûnferoarlike gegevens mei help fan pipelines (|), Where-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object, ensfh. - Jo hawwe in mear funksjonele styl en jo sille profitearje fan it brûken fan Powershell klassen yn in funksjonele styl.

Funksjoneel gebrûk fan klassen

Kasten, hoewol se in alternative syntaksis brûke, binne gewoan in mapping tusken twa domeinen. Yn 'e pipeline kinne jo in array fan wearden yn kaart bringe mei ForEach-Object.

Yn it foarbyld hjirûnder wurdt de Node constructor útfierd eltse kear in Datum wurdt cast, en dit jout ús de mooglikheid om net skriuwe in earlike bedrach fan koade. As gefolch, ús pipeline rjochtet him op deklarative gegevens querying en aggregaasje, wylst ús klassen soargje foar gegevens parsing en falidaasje.

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

Ferpakkingsklasse foar werbrûk

Neat is sa goed as it liket

Spitigernôch kinne klassen net eksportearre wurde troch modules op deselde wize as funksjes of fariabelen; mar der binne wat trúkjes. Litte wy sizze dat jo klassen binne definiearre yn it bestân ./my-classes.ps1

  • Jo kinne in bestân dotsource mei klassen:. ./my-classes.ps1. Dit sil my-classes.ps1 útfiere yn jo hjoeddeistige omfang en definiearje alle klassen út it bestân dêr.

  • Jo kinne in Powershell-module oanmeitsje dy't al jo oanpaste API's (cmdlets) eksportearret en de fariabele ScriptsToProcess = "./my-classes.ps1" yn jo modulemanifest ynstelle, mei itselde resultaat: ./my-classes.ps1 sil útfiere yn dyn omjouwing.

Hokker opsje jo ek kieze, hâld yn gedachten dat it typesysteem fan Powershell gjin soarten fan deselde namme kin oplosse dy't fan ferskate plakken laden binne.
Sels as jo twa identike klassen laden mei deselde eigenskippen fan ferskate plakken, riskearje jo dat jo yn problemen komme.

It paad foarút

De bêste manier om problemen mei type-resolúsje te foarkommen is om jo klassen noait oan brûkers bleat te stellen. Yn stee fan te ferwachtsjen dat de brûker in klasse-definiearre type ymportearret, eksportearje in funksje fan jo module dy't de needsaak om direkt tagong te krijen ta de klasse elimineert. Foar Cluster kinne wy ​​​​in New-Cluster-funksje eksportearje dy't brûkerfreonlike parametersets sil stypje en in Cluster weromjaan.

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

Wat oars te lêzen

Oer Klassen
Definsive PowerShell
Funksjonele programmearring yn PowerShell

Boarne: www.habr.com

Add a comment