Powershell berfungsi dengan kelas bukan oksimoron, saya menjaminnya

Hello, Habr! Saya membentangkan kepada anda terjemahan artikel "PowerShell Fungsian dengan Kelas.
Saya berjanji ia bukan oxymoron"
oleh Christopher Kuech.

Paradigma pengaturcaraan berorientasikan objek dan berfungsi mungkin kelihatan bertentangan antara satu sama lain, tetapi kedua-duanya disokong sama dalam Powershell. Hampir semua bahasa pengaturcaraan, berfungsi atau tidak, mempunyai kemudahan untuk mengikat nilai nama lanjutan; Kelas, seperti struct dan rekod, hanyalah satu pendekatan. Jika kami mengehadkan penggunaan Kelas kami kepada pengikatan nama dan nilai, dan mengelakkan konsep pengaturcaraan berorientasikan objek berat seperti pewarisan, polimorfisme atau kebolehubahan, kami boleh memanfaatkan faedahnya tanpa merumitkan kod kami. Selanjutnya, dengan menambahkan kaedah penukaran jenis tidak berubah, kami boleh memperkayakan kod fungsi kami dengan Kelas.

Keajaiban kasta

Kasta ialah salah satu ciri yang paling berkuasa dalam Powershell. Apabila anda menghantar nilai, anda bergantung pada keupayaan permulaan dan pengesahan tersirat yang ditambahkan oleh persekitaran pada aplikasi anda. Contohnya, hanya menghantar rentetan dalam [xml] akan menjalankannya melalui kod parser dan menjana pepohon xml yang lengkap. Kami boleh menggunakan Kelas dalam kod kami untuk tujuan yang sama.

Hantar jadual hash

Jika anda tidak mempunyai pembina, anda boleh meneruskan tanpa pembina dengan menghantar jadual hash ke jenis kelas anda. Jangan lupa untuk menggunakan atribut pengesahan untuk memanfaatkan sepenuhnya corak ini. Pada masa yang sama, kita boleh menggunakan sifat ditaip kelas untuk menjalankan logik permulaan dan pengesahan yang lebih mendalam.

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
}

Di samping itu, pemutus membantu untuk mendapatkan output yang bersih. Bandingkan output tatasusunan jadual cincang Kluster yang dihantar ke Jadual-Format dengan apa yang anda dapat jika anda mula-mula menghantar jadual cincang ini ke dalam kelas. Sifat kelas sentiasa disenaraikan dalam susunan di mana ia ditakrifkan di sana. Jangan lupa untuk menambah kata kunci tersembunyi sebelum semua sifat yang anda tidak mahu kelihatan dalam hasil carian.

Powershell berfungsi dengan kelas bukan oksimoron, saya menjaminnya

Lantunan makna

Jika anda mempunyai pembina dengan satu hujah, menghantar nilai kepada jenis kelas anda akan memberikan nilai kepada pembina anda, di mana anda boleh memulakan contoh kelas anda

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"

Hantar ke baris

Anda juga boleh mengatasi kaedah kelas [string] ToString() untuk menentukan logik di sebalik perwakilan rentetan objek, seperti menggunakan interpolasi rentetan.

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

Hantar contoh bersiri

Cast membenarkan penyahserikan yang selamat. Contoh di bawah akan gagal jika data tidak memenuhi spesifikasi kami dalam Kluster

# Валидация сСриализованных Π΄Π°Π½Π½Ρ‹Ρ…

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

Kasta dalam kod fungsi anda

Program fungsional mula-mula mentakrifkan struktur data, kemudian melaksanakan program sebagai urutan transformasi ke atas struktur data tidak berubah. Walaupun tanggapan yang bercanggah, kelas benar-benar membantu anda menulis kod berfungsi berkat kaedah penukaran jenis.

Adakah Powershell yang saya tulis berfungsi?

Ramai orang yang datang dari C# atau latar belakang yang serupa sedang menulis Powershell, yang serupa dengan C#. Dengan melakukan ini, anda akan beralih daripada menggunakan konsep pengaturcaraan berfungsi dan mungkin akan mendapat manfaat daripada menyelam secara meluas ke dalam pengaturcaraan berorientasikan objek dalam Powershell atau mempelajari lebih lanjut tentang pengaturcaraan berfungsi.

Jika anda sangat bergantung pada mengubah data tidak berubah menggunakan saluran paip (|), Where-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object, dsb. - anda mempunyai gaya yang lebih berfungsi dan anda akan mendapat manfaat daripada menggunakan Powershell kelas dalam gaya berfungsi.

Penggunaan kelas secara fungsional

Kasta, walaupun mereka menggunakan sintaks alternatif, hanyalah pemetaan antara dua domain. Dalam perancangan, anda boleh memetakan tatasusunan nilai menggunakan ForEach-Object.

Dalam contoh di bawah, pembina Nod dilaksanakan setiap kali Datum dibuang, dan ini memberi kita peluang untuk mengelak daripada menulis jumlah kod yang adil. Akibatnya, saluran paip kami menumpukan pada pertanyaan dan pengagregatan data deklaratif, manakala kelas kami mengurus penghuraian dan pengesahan data.

# ΠŸΡ€ΠΈΠΌΠ΅Ρ€ комбинирования классов с ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅Ρ€Π°ΠΌΠΈ для 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

Kelas pembungkusan untuk digunakan semula

Tidak ada yang sebaik yang kelihatan

Malangnya, kelas tidak boleh dieksport oleh modul dengan cara yang sama seperti fungsi atau pembolehubah; tetapi terdapat beberapa helah. Katakan kelas anda ditakrifkan dalam fail ./my-classes.ps1

  • Anda boleh dotsource fail dengan kelas:. ./my-classes.ps1. Ini akan melaksanakan my-classes.ps1 dalam skop semasa anda dan mentakrifkan semua kelas daripada fail di sana.

  • Anda boleh membuat modul Powershell yang mengeksport semua API tersuai anda (cmdlet) dan menetapkan pembolehubah ScriptsToProcess = "./my-classes.ps1" dalam manifes modul anda, dengan hasil yang sama: ./my-classes.ps1 akan dilaksanakan dalam persekitaran anda.

Mana-mana pilihan yang anda pilih, perlu diingat bahawa sistem jenis Powershell tidak dapat menyelesaikan jenis nama yang sama dimuatkan dari tempat yang berbeza.
Walaupun anda memuatkan dua kelas yang sama dengan sifat yang sama dari tempat yang berbeza, anda berisiko menghadapi masalah.

Jalan kehadapan

Cara terbaik untuk mengelakkan masalah resolusi jenis adalah dengan tidak sekali-kali mendedahkan kelas anda kepada pengguna. Daripada mengharapkan pengguna mengimport jenis yang ditentukan kelas, eksport fungsi daripada modul anda yang menghapuskan keperluan untuk mengakses kelas secara langsung. Untuk Kluster, kami boleh mengeksport fungsi Kluster Baharu yang akan menyokong set parameter mesra pengguna dan mengembalikan Kluster.

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

Apa lagi yang perlu dibaca

Mengenai Kelas
Defensive PowerShell
Pengaturcaraan Fungsian dalam PowerShell

Sumber: www.habr.com

Tambah komen