Funkcia Powershell kun klasoj ne estas oksimoron, mi garantias ĝin

Hej Habr! Mi prezentas al via atento la tradukon de la artikolo "Funkcia PowerShell kun Klasoj.
Mi promesas, ke ĝi ne estas oksimoron"
de Christopher Kuech.

Objekt-orientitaj kaj funkciaj programaj paradigmoj povas ŝajni en konflikto unu kun la alia, sed ambaŭ estas same subtenataj en Powershell. Preskaŭ ĉiuj programlingvoj, funkciaj aŭ ne, havas instalaĵojn por plilongigita nom-valora ligado; Klasoj, kiel strukturoj kaj rekordoj, estas nur unu aliro. Se ni limigas nian uzon de Klasoj al la ligado de nomoj kaj valoroj, kaj evitas pezajn objektorientajn programajn konceptojn kiel heredo, polimorfismo aŭ ŝanĝebleco, ni povas utiligi ilin sen kompliki nian kodon. Plue, aldonante neŝanĝeblajn tipkonvertajn metodojn, ni povas riĉigi nian funkcian kodon per Klasoj.

La magio de kastoj

Kastoj estas unu el la plej potencaj funkcioj en Powershell. Kiam vi ĵetas valoron, vi fidas je la implicitaj komencaj kaj validumaj kapabloj, kiujn la medio aldonas al via aplikaĵo. Ekzemple, simple ĵeti ĉenon en [xml] funkcios ĝin tra la analizkodo kaj generos kompletan xml-arbon. Ni povas uzi Klasojn en nia kodo por la sama celo.

Cast hashtables

Se vi ne havas konstruktilon, vi povas daŭrigi sen unu ĵetante hashtablon al via klastipo. Ne forgesu uzi la validigajn atributojn por plene profiti ĉi tiun ŝablonon. Samtempe, ni povas uzi la tajpitaj propraĵoj de la klaso por ruli eĉ pli profundan inicialigon kaj validigan logikon.

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
}

Krome, gisado helpas akiri puran produktadon. Komparu la eliron de la Cluster hashtable tabelo pasita al Format-Table kun tio, kion vi ricevas se vi unue ĵetas ĉi tiujn hashtables en klason. La propraĵoj de klaso ĉiam estas listigitaj en la ordo en kiu ili estas difinitaj tie. Ne forgesu aldoni la kaŝitan ŝlosilvorton antaŭ ĉiuj tiuj propraĵoj, kiujn vi ne volas esti videblaj en la rezultoj.

Funkcia Powershell kun klasoj ne estas oksimoron, mi garantias ĝin

Rolantaro de signifoj

Se vi havas konstruktilon kun unu argumento, ĵeti valoron al via klastipo transdonos la valoron al via konstrukciisto, kie vi povas pravalorigi ekzemplon de via klaso.

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"

Gisita al linio

Vi ankaŭ povas superregi la klasan metodon [string] ToString() por difini la logikon malantaŭ la ĉeno-reprezento de la objekto, kiel uzi ĉeninterpoladon.

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'"

Rondi seriigitajn petskribojn

Rolantaro permesas sekuran deserialigon. La malsupraj ekzemploj malsukcesos se la datumoj ne plenumas nian specifon en Cluster

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

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

Kastoj en via funkcia kodo

Funkciaj programoj unue difinas datumstrukturojn, tiam efektivigas la programon kiel sekvencon de transformoj super neŝanĝeblaj datumstrukturoj. Malgraŭ la kontraŭdira impreso, klasoj vere helpas vin skribi funkcian kodon danke al tipkonvertaj metodoj.

Ĉu la Powershell, kiun mi skribas, funkcias?

Multaj homoj venantaj de C# aŭ similaj fonoj verkas Powershell, kiu similas al C#. Farante tion, vi foriras de uzado de funkciaj programaj konceptoj kaj verŝajne profitus el plonĝado peze en objekt-orientitan programadon en Powershell aŭ lerni pli pri funkcia programado.

Se vi multe dependas de transformado de neŝanĝeblaj datumoj per duktoj (|), Kie-Objekto, PorĈiu-Objekto, Elekto-Objekto, Grupo-Objekto, Ordigo-Objekto, ktp. - vi havas pli funkcian stilon kaj vi profitos de uzado de Powershell. klasoj en funkcia stilo.

Funkcia uzo de klasoj

Kastoj, kvankam ili uzas alternativan sintakson, estas nur mapado inter du domajnoj. En la dukto, vi povas mapi aron da valoroj uzante ForEach-Object.

En la suba ekzemplo, la Node-konstrukciisto estas ekzekutita ĉiufoje kiam Datumo estas ĵetita, kaj ĉi tio donas al ni la ŝancon eviti skribi sufiĉe da kodo. Kiel rezulto, nia dukto temigas deklarajn datumojn pri demandado kaj agregado, dum niaj klasoj prizorgas datuman analizon kaj validigon.

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

Klaso de pakaĵo por reuzo

Nenio estas tiel bona kiel ŝajnas

Bedaŭrinde, klasoj ne povas esti eksportitaj per moduloj en la sama maniero kiel funkcioj aŭ variabloj; sed estas kelkaj lertaĵoj. Ni diru, ke viaj klasoj estas difinitaj en la dosiero ./my-classes.ps1

  • Vi povas dotsource dosieron kun klasoj:. ./my-classes.ps1. Ĉi tio ekzekutos my-classes.ps1 en via nuna amplekso kaj difinos ĉiujn klasojn de la dosiero tie.

  • Vi povas krei Powershell-modulon, kiu eksportas ĉiujn viajn kutimajn API-ojn (cmdlets) kaj agordi la variablon ScriptsToProcess = "./my-classes.ps1" en via modula manifesto, kun la sama rezulto: ./my-classes.ps1 efektiviĝos en via medio.

Kiun ajn opcion vi elektas, memoru, ke la tipsistemo de Powershell ne povas solvi samnomajn tipojn ŝarĝitajn de malsamaj lokoj.
Eĉ se vi ŝargis du identajn klasojn kun la samaj propraĵoj de malsamaj lokoj, vi riskas renkonti problemojn.

La vojo antaŭen

La plej bona maniero eviti problemojn pri tipa rezolucio estas neniam elmontri viajn klasojn al uzantoj. Anstataŭ atendi, ke la uzanto importos klas-difinitan tipon, eksportu funkcion el via modulo, kiu forigas la bezonon rekte aliri la klason. Por Cluster, ni povas eksporti Nova-Cluster-funkcion, kiu subtenos uzeblajn parametrojn kaj resendos 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

Kion alian legi

Pri Klasoj
Defenda PowerShell
Funkcia Programado en PowerShell

fonto: www.habr.com

Aldoni komenton