Toiminnallinen Powershell luokkien kanssa ei ole oksymoroni, takaan sen

Hei Habr! Esitän huomionne artikkelin käännöksen "Toimiva PowerShell luokilla.
Lupaan, että se ei ole oksymoroni"
Kirjailija: Christopher Kuech

Oliosuuntautunut ja toiminnallinen ohjelmointiparadigmat voivat tuntua ristiriidassa keskenään, mutta Powershell tukee molempia yhtä hyvin. Lähes kaikissa ohjelmointikielissä, toimivissa tai ei, on mahdollisuudet laajennettuun nimiarvosidontaan; Luokat, kuten rakenteet ja tietueet, ovat vain yksi lähestymistapa. Jos rajoitamme luokkien käytön nimien ja arvojen sitomiseen ja vältämme raskaita olio-ohjelmointikonsepteja, kuten periytymistä, polymorfismia tai muuntuvuutta, voimme hyödyntää niiden etuja mutkistamatta koodiamme. Lisäksi lisäämällä muuttumattomia tyyppimuunnosmenetelmiä voimme rikastaa toiminnallista koodiamme luokilla.

Kastien taikuutta

Kastit ovat yksi Powershellin tehokkaimmista ominaisuuksista. Kun annat arvon, luotat implisiittisiin alustus- ja vahvistusominaisuuksiin, joita ympäristö lisää sovellukseesi. Esimerkiksi merkkijonon lähettäminen [xml]:ssä ajaa sen jäsennyskoodin läpi ja luo täydellisen xml-puun. Voimme käyttää koodissamme luokkia samaan tarkoitukseen.

Cast hashtables

Jos sinulla ei ole rakentajaa, voit jatkaa ilman sitä lähettämällä hashtable luokkatyyppiisi. Älä unohda käyttää vahvistusattribuutteja saadaksesi täyden hyödyn tästä mallista. Samalla voimme käyttää luokan kirjoitettuja ominaisuuksia ajaaksemme entistä syvempää alustus- ja validointilogiikkaa.

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
}

Lisäksi valu auttaa saamaan puhtaan lopputuloksen. Vertaa Format-Tablelle välitetyn klusterin hashtable-taulukon tulosta siihen, mitä saat, jos lähetät nämä hashtablet ensin luokkaan. Luokan ominaisuudet luetellaan aina siinä järjestyksessä, jossa ne on määritelty. Älä unohda lisätä piilotettua avainsanaa ennen kaikkia niitä ominaisuuksia, joiden et halua näkyvän tuloksissa.

Toiminnallinen Powershell luokkien kanssa ei ole oksymoroni, takaan sen

Valinta merkityksiä

Jos sinulla on konstruktori yhdellä argumentilla, arvon lähettäminen luokkatyyppiin välittää arvon konstruktorillesi, jossa voit alustaa luokkasi esiintymän

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"

Cast riviin

Voit myös ohittaa [string] ToString()-luokan menetelmän määrittääksesi logiikan objektin merkkijonoesityksen takana, kuten käyttämällä merkkijonointerpolaatiota.

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 sarjoitetut esiintymät

Cast mahdollistaa turvallisen deserialisoinnin. Alla olevat esimerkit epäonnistuvat, jos tiedot eivät täytä klusterin määrityksiämme

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

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

Castes toiminnallisessa koodissasi

Toiminnalliset ohjelmat määrittelevät ensin tietorakenteet ja toteuttavat sitten ohjelman muunnossarjana muuttumattomien tietorakenteiden yli. Ristiriitaisesta vaikutelmasta huolimatta luokat todella auttavat sinua kirjoittamaan toiminnallista koodia tyyppimuunnosmenetelmien ansiosta.

Onko kirjoittamani Powershell toimiva?

Monet C#:sta tai vastaavasta taustasta tulevat ihmiset kirjoittavat Powershelliä, joka on samanlainen kuin C#. Tekemällä tämän siirryt pois toiminnallisten ohjelmointikonseptien käytöstä ja todennäköisesti hyötyisit sukeltamasta voimakkaasti Powershell-olioohjelmointiin tai oppimaan lisää toiminnallisesta ohjelmoinnista.

Jos luotat voimakkaasti muuttumattomien tietojen muuntamiseen liukuputkien (|), Where-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object jne. avulla - sinulla on toiminnallisempi tyyli ja hyödyt Powershellin käytöstä tunnit toiminnallisella tyylillä.

Luokkien toiminnallinen käyttö

Kastit, vaikka ne käyttävät vaihtoehtoista syntaksia, ovat vain kartoitus kahden toimialueen välillä. Valmistelussa voit kartoittaa joukon arvoja ForEach-Objectin avulla.

Alla olevassa esimerkissä solmukonstruktori suoritetaan joka kerta, kun Datum heitetään, ja tämä antaa meille mahdollisuuden olla kirjoittamatta kohtuullista määrää koodia. Tämän seurauksena putkistomme keskittyy deklaratiiviseen tietojen kyselyyn ja yhdistämiseen, kun taas luokat huolehtivat tietojen jäsentämisestä ja validoinnista.

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

Pakkausluokka uudelleenkäyttöön

Mikään ei ole niin hyvä kuin miltä näyttää

Valitettavasti luokkia ei voi viedä moduuleittain samalla tavalla kuin funktioita tai muuttujia; mutta on joitain temppuja. Oletetaan, että luokat on määritetty tiedostossa ./my-classes.ps1

  • Voit luoda pistelähteen tiedoston luokilla:. ./my-classes.ps1. Tämä suorittaa my-classes.ps1:n nykyisessä laajuudessasi ja määrittää kaikki luokat siellä olevasta tiedostosta.

  • Voit luoda Powershell-moduulin, joka vie kaikki mukautetut sovellusliittymäsi (cmdletit) ja asettaa ScriptsToProcess = "./my-classes.ps1" -muuttujan moduulin luetteloon samalla tuloksella: ./my-classes.ps1 suoritetaan ympäristösi.

Valitsetpa minkä vaihtoehdon tahansa, muista, että Powershellin tyyppijärjestelmä ei pysty ratkaisemaan eri paikoista ladattuja samannimisen tyyppejä.
Vaikka ladasit kaksi identtistä luokkaa, joilla on samat ominaisuudet eri paikoista, voit joutua ongelmiin.

Tie eteenpäin

Paras tapa välttää tyypin resoluutioongelmia on olla koskaan paljastamatta luokkiasi käyttäjille. Sen sijaan, että odottaisit käyttäjän tuovan luokan määrittämän tyypin, vie moduulista funktio, joka eliminoi tarpeen käyttää luokkaa suoraan. Clusterille voimme viedä New-Cluster-funktion, joka tukee käyttäjäystävällisiä parametrijoukkoja ja palauttaa klusterin.

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

Mitä muuta luettavaa

Tietoja luokista
Puolustava PowerShell
Toiminnallinen ohjelmointi PowerShellissä

Lähde: will.com

Lisää kommentti