|
| 1 | +class Book { |
| 2 | + # Class properties |
| 3 | + [string] $Title |
| 4 | + [string] $Author |
| 5 | + [string] $Synopsis |
| 6 | + [string] $Publisher |
| 7 | + [datetime] $PublishDate |
| 8 | + [int] $PageCount |
| 9 | + [string[]] $Tags |
| 10 | + # Default constructor |
| 11 | + Book() { $this.Init(@{}) } |
| 12 | + # Convenience constructor from hashtable |
| 13 | + Book([hashtable]$Properties) { $this.Init($Properties) } |
| 14 | + # Common constructor for title and author |
| 15 | + Book([string]$Title, [string]$Author) { |
| 16 | + $this.Init(@{Title = $Title; Author = $Author }) |
| 17 | + } |
| 18 | + # Shared initializer method |
| 19 | + [void] Init([hashtable]$Properties) { |
| 20 | + foreach ($Property in $Properties.Keys) { |
| 21 | + $this.$Property = $Properties.$Property |
| 22 | + } |
| 23 | + } |
| 24 | + # Method to calculate reading time as 2 minutes per page |
| 25 | + [timespan] GetReadingTime() { |
| 26 | + if ($this.PageCount -le 0) { |
| 27 | + throw 'Unable to determine reading time from page count.' |
| 28 | + } |
| 29 | + $Minutes = $this.PageCount * 2 |
| 30 | + return [timespan]::new(0, $Minutes, 0) |
| 31 | + } |
| 32 | + # Method to calculate how long ago a book was published |
| 33 | + [timespan] GetPublishedAge() { |
| 34 | + if ( |
| 35 | + $null -eq $this.PublishDate -or |
| 36 | + $this.PublishDate -eq [datetime]::MinValue |
| 37 | + ) { throw 'PublishDate not defined' } |
| 38 | + |
| 39 | + return (Get-Date) - $this.PublishDate |
| 40 | + } |
| 41 | + # Method to return a string representation of the book |
| 42 | + [string] ToString() { |
| 43 | + return "$($this.Title) by $($this.Author) ($($this.PublishDate.Year))" |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +class BookList { |
| 48 | + # Static property to hold the list of books |
| 49 | + static [System.Collections.Generic.List[Book]] $Books |
| 50 | + # Static method to initialize the list of books. Called in the other |
| 51 | + # static methods to avoid needing to explicit initialize the value. |
| 52 | + static [void] Initialize() { [BookList]::Initialize($false) } |
| 53 | + static [bool] Initialize([bool]$force) { |
| 54 | + if ([BookList]::Books.Count -gt 0 -and -not $force) { |
| 55 | + return $false |
| 56 | + } |
| 57 | + |
| 58 | + [BookList]::Books = [System.Collections.Generic.List[Book]]::new() |
| 59 | + |
| 60 | + return $true |
| 61 | + } |
| 62 | + # Ensure a book is valid for the list. |
| 63 | + static [void] Validate([book]$Book) { |
| 64 | + $Prefix = @( |
| 65 | + 'Book validation failed: Book must be defined with the Title,' |
| 66 | + 'Author, and PublishDate properties, but' |
| 67 | + ) -join ' ' |
| 68 | + if ($null -eq $Book) { throw "$Prefix was null" } |
| 69 | + if ([string]::IsNullOrEmpty($Book.Title)) { |
| 70 | + throw "$Prefix Title wasn't defined" |
| 71 | + } |
| 72 | + if ([string]::IsNullOrEmpty($Book.Author)) { |
| 73 | + throw "$Prefix Author wasn't defined" |
| 74 | + } |
| 75 | + if ([datetime]::MinValue -eq $Book.PublishDate) { |
| 76 | + throw "$Prefix PublishDate wasn't defined" |
| 77 | + } |
| 78 | + } |
| 79 | + # Static methods to manage the list of books. |
| 80 | + # Add a book if it's not already in the list. |
| 81 | + static [void] Add([Book]$Book) { |
| 82 | + [BookList]::Initialize() |
| 83 | + [BookList]::Validate($Book) |
| 84 | + if ([BookList]::Books.Contains($Book)) { |
| 85 | + throw "Book '$Book' already in list" |
| 86 | + } |
| 87 | + |
| 88 | + $FindPredicate = { |
| 89 | + param([Book]$b) |
| 90 | + |
| 91 | + $b.Title -eq $Book.Title -and |
| 92 | + $b.Author -eq $Book.Author -and |
| 93 | + $b.PublishDate -eq $Book.PublishDate |
| 94 | + }.GetNewClosure() |
| 95 | + if ([BookList]::Books.Find($FindPredicate)) { |
| 96 | + throw "Book '$Book' already in list" |
| 97 | + } |
| 98 | + |
| 99 | + [BookList]::Books.Add($Book) |
| 100 | + } |
| 101 | + # Clear the list of books. |
| 102 | + static [void] Clear() { |
| 103 | + [BookList]::Initialize() |
| 104 | + [BookList]::Books.Clear() |
| 105 | + } |
| 106 | + # Find a specific book using a filtering scriptblock. |
| 107 | + static [Book] Find([scriptblock]$Predicate) { |
| 108 | + [BookList]::Initialize() |
| 109 | + return [BookList]::Books.Find($Predicate) |
| 110 | + } |
| 111 | + # Find every book matching the filtering scriptblock. |
| 112 | + static [Book[]] FindAll([scriptblock]$Predicate) { |
| 113 | + [BookList]::Initialize() |
| 114 | + return [BookList]::Books.FindAll($Predicate) |
| 115 | + } |
| 116 | + # Remove a specific book. |
| 117 | + static [void] Remove([Book]$Book) { |
| 118 | + [BookList]::Initialize() |
| 119 | + [BookList]::Books.Remove($Book) |
| 120 | + } |
| 121 | + # Remove a book by property value. |
| 122 | + static [void] RemoveBy([string]$Property, [string]$Value) { |
| 123 | + [BookList]::Initialize() |
| 124 | + $Index = [BookList]::Books.FindIndex({ |
| 125 | + param($b) |
| 126 | + $b.$Property -eq $Value |
| 127 | + }.GetNewClosure()) |
| 128 | + if ($Index -ge 0) { |
| 129 | + [BookList]::Books.RemoveAt($Index) |
| 130 | + } |
| 131 | + } |
| 132 | +} |
0 commit comments