You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

252 lines
11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

open System
open System.Text.RegularExpressions
type DocumentType =
| Passport
| DriverLicense
| InternationalPassport
| IdentityCard
type ValidationResult =
| Valid
| InvalidSeries of string
| InvalidNumber of string
| InvalidIssueDate of string
| InvalidExpiryDate of string
| InvalidIssuedBy of string
[<AllowNullLiteral>]
type Document(docType: DocumentType, series: string, number: string, issuedBy: string, issueDate: DateTime, expiryDate: DateTime option) =
let mutable _series = series
let mutable _number = number
let mutable _issuedBy = issuedBy
let mutable _issueDate = issueDate
let mutable _expiryDate = expiryDate
let seriesPattern = @"^\d{4}$"
let numberPattern = @"^\d{6}$"
let issuedByPattern = @"^[А-Яа-яA-Za-z\s\-\.]{5,100}$"
let validateSeries (s: string) =
if String.IsNullOrWhiteSpace(s) then false
else Regex.IsMatch(s, seriesPattern)
let validateNumber (n: string) =
if String.IsNullOrWhiteSpace(n) then false
else Regex.IsMatch(n, numberPattern)
let validateIssuedBy (issuer: string) =
if String.IsNullOrWhiteSpace(issuer) then false
else Regex.IsMatch(issuer, issuedByPattern)
let validateDates (issue: DateTime) (expiry: DateTime option) =
let now = DateTime.Now
match expiry with
| Some exp -> issue <= now && issue < exp
| None -> issue <= now
let validate() =
if not (validateSeries _series) then InvalidSeries "Series must be 4 digits"
elif not (validateNumber _number) then InvalidNumber "Number must be 6 digits"
elif not (validateIssuedBy _issuedBy) then InvalidIssuedBy "Invalid issuer format"
elif not (validateDates _issueDate _expiryDate) then InvalidIssueDate "Invalid dates"
else Valid
do
let validation = validate()
match validation with
| Valid -> ()
| _ -> failwithf "Document validation failed: %A" validation
member this.DocumentType = docType
member this.Series
with get() = _series
and set(value) =
if validateSeries value then _series <- value
else raise (ArgumentException("Invalid series format. Must be 4 digits."))
member this.Number
with get() = _number
and set(value) =
if validateNumber value then _number <- value
else raise (ArgumentException("Invalid number format. Must be 6 digits."))
member this.IssuedBy
with get() = _issuedBy
and set(value) =
if validateIssuedBy value then _issuedBy <- value
else raise (ArgumentException("Invalid issuer format."))
member this.IssueDate
with get() = _issueDate
and set(value) =
if validateDates value _expiryDate then _issueDate <- value
else raise (ArgumentException("Invalid issue date."))
member this.ExpiryDate
with get() = _expiryDate
and set(value) =
if validateDates _issueDate value then _expiryDate <- value
else raise (ArgumentException("Invalid expiry date."))
member this.Validate() = validate()
member this.IsValid = this.Validate() = Valid
member this.Display() =
let docTypeStr =
match docType with
| Passport -> "Паспорт"
| DriverLicense -> "Водительские права"
| InternationalPassport -> "Загранпаспорт"
| IdentityCard -> "Удостоверение личности"
let expiryStr =
match _expiryDate with
| Some date -> date.ToString("dd.MM.yyyy")
| None -> "Бессрочно"
printfn "┌─────────────────────────────────────┐"
printfn "│ %-35s │" docTypeStr
printfn "├─────────────────────────────────────┤"
printfn "│ Серия: %-28s │" _series
printfn "│ Номер: %-28s │" _number
printfn "│ Выдан: %-28s │" _issuedBy
printfn "│ Дата выдачи: %-19s │" (_issueDate.ToString("dd.MM.yyyy"))
printfn "│ Действителен до: %-15s │" expiryStr
printfn "└─────────────────────────────────────┘"
override this.ToString() =
sprintf "%A %s-%s" docType _series _number
interface IComparable with
member this.CompareTo(obj) =
match obj with
| :? Document as other -> this.CompareTo(other)
| _ -> invalidArg "obj" "Cannot compare with non-Document object"
member this.CompareTo(other: Document) =
if other = null then 1
else
let seriesComparison = String.Compare(_series, other.Series)
if seriesComparison <> 0 then seriesComparison
else String.Compare(_number, other.Number)
interface IComparable<Document> with
member this.CompareTo(other) = this.CompareTo(other)
override this.Equals(obj) =
match obj with
| :? Document as other ->
_series = other.Series && _number = other.Number
| _ -> false
override this.GetHashCode() =
hash (_series, _number)
static member TryCreate(docType: DocumentType, series: string, number: string, issuedBy: string, issueDate: DateTime, expiryDate: DateTime option) =
try
let doc = Document(docType, series, number, issuedBy, issueDate, expiryDate)
Some doc
with
| _ -> None
static member op_Equality(left: Document, right: Document) =
if left = null && right = null then true
elif left = null || right = null then false
else left.Equals(right)
static member op_Inequality(left: Document, right: Document) =
not (left = right)
static member op_LessThan(left: Document, right: Document) =
if left = null then right <> null
elif right = null then false
else (left :> IComparable<Document>).CompareTo(right) < 0
static member op_GreaterThan(left: Document, right: Document) =
if right = null then left <> null
elif left = null then false
else (left :> IComparable<Document>).CompareTo(right) > 0
module DocumentTests =
let testDocumentCreation() =
printfn "=== Тест создания документов ==="
let passport = Document(Passport, "1234", "567890", "УМВД России по г. Москве", DateTime(2020, 5, 15), None)
let license = Document(DriverLicense, "5678", "123456", "ГИБДД УМВД России", DateTime(2021, 3, 10), Some(DateTime(2031, 3, 10)))
passport.Display()
printfn ""
license.Display()
printfn ""
let testValidation() =
printfn "=== Тест валидации ==="
let validCases = [
("Valid passport", Passport, "1234", "567890", "УМВД России по г. Москве", DateTime(2020, 5, 15), None)
("Valid license", DriverLicense, "5678", "123456", "ГИБДД УМВД России", DateTime(2021, 3, 10), Some(DateTime(2031, 3, 10)))
]
let invalidCases = [
("Invalid series", Passport, "12", "567890", "УМВД России", DateTime(2020, 5, 15), None)
("Invalid number", Passport, "1234", "56789", "УМВД России", DateTime(2020, 5, 15), None)
("Invalid issuer", Passport, "1234", "567890", "УМ", DateTime(2020, 5, 15), None)
]
printfn "Валидные документы:"
for (description, docType, series, number, issuedBy, issueDate, expiryDate) in validCases do
match Document.TryCreate(docType, series, number, issuedBy, issueDate, expiryDate) with
| Some doc -> printfn "✓ %s: %s" description (doc.ToString())
| None -> printfn "✗ %s: Ошибка создания" description
printfn "\nНевалидные документы:"
for (description, docType, series, number, issuedBy, issueDate, expiryDate) in invalidCases do
match Document.TryCreate(docType, series, number, issuedBy, issueDate, expiryDate) with
| Some doc -> printfn "✗ %s: Неожиданно валиден: %s" description (doc.ToString())
| None -> printfn "✓ %s: Корректно отклонен" description
printfn ""
let testComparison() =
printfn "=== Тест сравнения документов ==="
let doc1 = Document(Passport, "1234", "567890", "УМВД России", DateTime(2020, 5, 15), None)
let doc2 = Document(Passport, "1234", "567890", "Другой орган", DateTime(2021, 1, 1), None)
let doc3 = Document(DriverLicense, "5678", "123456", "ГИБДД", DateTime(2021, 3, 10), Some(DateTime(2031, 3, 10)))
let doc4 = Document(Passport, "1235", "567890", "УМВД России", DateTime(2020, 5, 15), None)
printfn "doc1 = doc2 (одинаковые серия и номер): %b" (doc1 = doc2)
printfn "doc1 = doc3 (разные серия и номер): %b" (doc1 = doc3)
printfn "doc1 < doc4 (сравнение по серии): %b" (doc1 < doc4)
printfn "doc4 > doc1 (сравнение по серии): %b" (doc4 > doc1)
let documents = [| doc1; doc3; doc4; doc2 |]
printfn "\nДо сортировки:"
documents |> Array.iter (fun doc -> printfn " %s" (doc.ToString()))
let sortedDocs = documents |> Array.sort
printfn "\nПосле сортировки:"
sortedDocs |> Array.iter (fun doc -> printfn " %s" (doc.ToString()))
printfn ""
[<EntryPoint>]
let main argv =
printfn "Лабораторная работа 7: ООП - Класс Document"
printfn "==========================================\n"
DocumentTests.testDocumentCreation()
DocumentTests.testValidation()
DocumentTests.testComparison()
printfn "=== Интерактивный тест ==="
try
printfn "Создание документа с некорректными данными..."
let invalidDoc = Document(Passport, "123", "567890", "УМВД", DateTime(2020, 5, 15), None)
printfn "Документ создан (не должно было произойти): %s" (invalidDoc.ToString())
with
| ex -> printfn "✓ Корректно перехвачена ошибка: %s" ex.Message
0