Hei Habr! Untuk perhatian Anda, saya persembahkan terjemahan artikel tersebut
Aku berjanji itu bukan sebuah oxymoron"
Paradigma pemrograman berorientasi objek dan fungsional mungkin tampak bertentangan satu sama lain, namun keduanya sama-sama didukung di Powershell. Hampir semua bahasa pemrograman, berfungsi atau tidak, memiliki fasilitas untuk pengikatan nama-nilai yang diperluas; Kelas, seperti struct dan record, hanyalah satu pendekatan. Jika kita membatasi penggunaan Kelas pada pengikatan nama dan nilai, dan menghindari konsep pemrograman berorientasi objek yang berat seperti pewarisan, polimorfisme, atau mutabilitas, kita dapat memanfaatkan manfaatnya tanpa mempersulit kode kita. Selanjutnya, dengan menambahkan metode konversi tipe yang tidak dapat diubah, kita dapat memperkaya kode fungsional kita dengan Kelas.
Keajaiban kasta
Kasta adalah salah satu fitur paling kuat di Powershell. Saat Anda memberikan nilai, Anda mengandalkan kemampuan inisialisasi dan validasi implisit yang ditambahkan lingkungan ke aplikasi Anda. Misalnya, memasukkan string ke dalam [xml] akan menjalankannya melalui kode parser dan menghasilkan pohon xml lengkap. Kita dapat menggunakan Kelas dalam kode kita untuk tujuan yang sama.
Keluarkan tabel hash
Jika Anda tidak memiliki konstruktor, Anda dapat melanjutkan tanpa konstruktor dengan memasukkan tabel hash ke tipe kelas Anda. Jangan lupa untuk menggunakan atribut validasi untuk memanfaatkan pola ini sepenuhnya. Pada saat yang sama, kita dapat menggunakan properti kelas yang diketik untuk menjalankan logika inisialisasi dan validasi yang lebih dalam.
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
}
Selain itu, casting membantu mendapatkan hasil yang bersih. Bandingkan output dari array hashtable Cluster yang diteruskan ke Format-Table dengan apa yang Anda dapatkan jika Anda pertama kali memasukkan tabel hash ini ke dalam kelas. Properti suatu kelas selalu dicantumkan sesuai urutan definisinya di sana. Jangan lupa untuk menambahkan kata kunci tersembunyi sebelum semua properti yang Anda tidak ingin terlihat di hasil.
Pemeran makna
Jika Anda memiliki konstruktor dengan satu argumen, memberikan nilai ke tipe kelas Anda akan meneruskan nilai tersebut ke konstruktor Anda, tempat Anda dapat menginisialisasi turunan 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"
Transmisikan ke garis
Anda juga dapat mengganti metode kelas [string] ToString() untuk menentukan logika di balik representasi string objek, seperti menggunakan interpolasi string.
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'"
Transmisikan instance serial
Pemeran memungkinkan deserialisasi yang aman. Contoh di bawah ini akan gagal jika data tidak memenuhi spesifikasi kami di Cluster
# ΠΠ°Π»ΠΈΠ΄Π°ΡΠΈΡ ΡΠ΅ΡΠΈΠ°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π½ΡΡ
Π΄Π°Π½Π½ΡΡ
[Cluster]$cluster = Get-Content "./my-cluster.json" | ConvertFrom-Json
[Cluster[]]$clusters = Import-Csv "./my-clusters.csv"
Kasta dalam kode fungsional Anda
Program fungsional pertama-tama mendefinisikan struktur data, kemudian mengimplementasikan program sebagai rangkaian transformasi pada struktur data yang tidak dapat diubah. Meskipun kesannya kontradiktif, kelas sangat membantu Anda menulis kode fungsional berkat metode konversi tipe.
Apakah Powershell yang saya tulis berfungsi?
Banyak orang yang berasal dari C# atau latar belakang serupa menulis Powershell, yang mirip dengan C#. Dengan melakukan ini, Anda beralih dari penggunaan konsep pemrograman fungsional dan kemungkinan besar akan mendapat manfaat dari mendalami pemrograman berorientasi objek di Powershell atau mempelajari lebih lanjut tentang pemrograman fungsional.
Jika Anda sangat bergantung pada transformasi data yang tidak dapat diubah menggunakan saluran pipa (|), Where-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object, dll. - Anda memiliki gaya yang lebih fungsional dan Anda akan mendapat manfaat dari penggunaan Powershell kelas dalam gaya fungsional.
Penggunaan kelas secara fungsional
Kasta, meskipun menggunakan sintaksis alternatif, hanyalah pemetaan antara dua domain. Di dalam pipeline, Anda dapat memetakan array nilai menggunakan ForEach-Object.
Pada contoh di bawah, konstruktor Node dieksekusi setiap kali Datum dilemparkan, dan ini memberi kita kesempatan untuk tidak menulis kode dalam jumlah yang cukup. Hasilnya, pipeline kami berfokus pada kueri dan agregasi data deklaratif, sementara kelas kami menangani penguraian dan validasi 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 pengemasan untuk digunakan kembali
Tidak ada yang sebaik kelihatannya
Sayangnya, kelas tidak dapat diekspor oleh modul dengan cara yang sama seperti fungsi atau variabel; tapi ada beberapa trik. Katakanlah kelas Anda didefinisikan dalam file ./my-classes.ps1
-
Anda dapat melakukan dotsource file dengan kelas :. ./kelas-saya.ps1. Ini akan mengeksekusi my-classes.ps1 dalam lingkup Anda saat ini dan mendefinisikan semua kelas dari file di sana.
-
Anda dapat membuat modul Powershell yang mengekspor semua API khusus (cmdlet) dan menyetel variabel ScriptsToProcess = "./my-classes.ps1" di manifes modul Anda, dengan hasil yang sama: ./my-classes.ps1 akan dijalankan di lingkungan Anda.
Opsi mana pun yang Anda pilih, perlu diingat bahwa sistem tipe Powershell tidak dapat menyelesaikan tipe dengan nama yang sama yang dimuat dari tempat berbeda.
Bahkan jika Anda memuat dua kelas identik dengan properti yang sama dari tempat berbeda, Anda berisiko mengalami masalah.
Jalan lurus
Cara terbaik untuk menghindari masalah resolusi tipe adalah dengan tidak pernah memaparkan kelas Anda kepada pengguna. Daripada mengharapkan pengguna mengimpor tipe yang ditentukan kelas, ekspor fungsi dari modul Anda yang menghilangkan kebutuhan untuk mengakses kelas secara langsung. Untuk Cluster, kita dapat mengekspor fungsi New-Cluster yang akan mendukung kumpulan parameter yang mudah digunakan dan mengembalikan 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
Apa lagi yang harus dibaca?
Sumber: www.habr.com