Layer Supertype (Супертип Уровня)
Описание Layer Supertype
Тип, выступающий родительским для всех типов в своём уровне
Нередко все классы одного уровня имеют одинаковые методы, которые не хочется дублировать повсеместно. Для того, чтобы избежать дублирования, можно все общие методы перенести в один класс (Layer Supertype), который будет являться Супертипом (читай - родителем) всех классов в своём уровне.
Примеры реализации
// Layer Supertype Pattern in JavaScript
class DomainObject {
constructor(id) {
this.id = id;
this.createdAt = new Date();
this.updatedAt = new Date();
}
getId() {
return this.id;
}
getCreatedAt() {
return this.createdAt;
}
getUpdatedAt() {
return this.updatedAt;
}
updateTimestamp() {
this.updatedAt = new Date();
}
validate() {
// Base validation - can be overridden
return this.id != null && this.id > 0;
}
save() {
if (this.validate()) {
console.log(`Saving ${this.constructor.name} with ID: ${this.id}`);
this.updateTimestamp();
return true;
}
return false;
}
delete() {
console.log(`Deleting ${this.constructor.name} with ID: ${this.id}`);
return true;
}
}
// Domain entities that extend the supertype
class User extends DomainObject {
constructor(id, name, email) {
super(id);
this.name = name;
this.email = email;
}
validate() {
return super.validate() &&
this.name &&
this.email &&
this.email.includes('@');
}
getDisplayName() {
return `${this.name} (${this.email})`;
}
}
class Product extends DomainObject {
constructor(id, name, price) {
super(id);
this.name = name;
this.price = price;
}
validate() {
return super.validate() &&
this.name &&
this.price > 0;
}
getFormattedPrice() {
return `$${this.price.toFixed(2)}`;
}
}
// Usage
const user = new User(1, 'John Doe', 'john@example.com');
const product = new Product(1, 'Laptop', 999.99);
console.log('User validation:', user.validate());
console.log('User display:', user.getDisplayName());
user.save();
console.log('Product validation:', product.validate());
console.log('Product price:', product.getFormattedPrice());
product.save();
<?php
// Layer Supertype Pattern in PHP
abstract class DomainObject {
protected $id;
protected $createdAt;
protected $updatedAt;
public function __construct($id) {
$this->id = $id;
$this->createdAt = new DateTime();
$this->updatedAt = new DateTime();
}
public function getId() {
return $this->id;
}
public function getCreatedAt() {
return $this->createdAt;
}
public function getUpdatedAt() {
return $this->updatedAt;
}
protected function updateTimestamp() {
$this->updatedAt = new DateTime();
}
public function validate() {
// Base validation - can be overridden
return $this->id !== null && $this->id > 0;
}
public function save() {
if ($this->validate()) {
echo "Saving " . get_class($this) . " with ID: {$this->id}\n";
$this->updateTimestamp();
return true;
}
return false;
}
public function delete() {
echo "Deleting " . get_class($this) . " with ID: {$this->id}\n";
return true;
}
}
// Domain entities that extend the supertype
class User extends DomainObject {
private $name;
private $email;
public function __construct($id, $name, $email) {
parent::__construct($id);
$this->name = $name;
$this->email = $email;
}
public function validate() {
return parent::validate() &&
!empty($this->name) &&
!empty($this->email) &&
strpos($this->email, '@') !== false;
}
public function getDisplayName() {
return "{$this->name} ({$this->email})";
}
}
class Product extends DomainObject {
private $name;
private $price;
public function __construct($id, $name, $price) {
parent::__construct($id);
$this->name = $name;
$this->price = $price;
}
public function validate() {
return parent::validate() &&
!empty($this->name) &&
$this->price > 0;
}
public function getFormattedPrice() {
return '$' . number_format($this->price, 2);
}
}
// Usage
$user = new User(1, 'John Doe', 'john@example.com');
$product = new Product(1, 'Laptop', 999.99);
echo "User validation: " . ($user->validate() ? 'true' : 'false') . "\n";
echo "User display: " . $user->getDisplayName() . "\n";
$user->save();
echo "Product validation: " . ($product->validate() ? 'true' : 'false') . "\n";
echo "Product price: " . $product->getFormattedPrice() . "\n";
$product->save();
?>
// Layer Supertype Pattern in Go
package main
import (
"fmt"
"time"
)
type DomainObject struct {
ID int
CreatedAt time.Time
UpdatedAt time.Time
}
func NewDomainObject(id int) *DomainObject {
now := time.Now()
return &DomainObject{
ID: id,
CreatedAt: now,
UpdatedAt: now,
}
}
func (do *DomainObject) GetID() int {
return do.ID
}
func (do *DomainObject) GetCreatedAt() time.Time {
return do.CreatedAt
}
func (do *DomainObject) GetUpdatedAt() time.Time {
return do.UpdatedAt
}
func (do *DomainObject) UpdateTimestamp() {
do.UpdatedAt = time.Now()
}
func (do *DomainObject) Validate() bool {
// Base validation - can be overridden
return do.ID > 0
}
func (do *DomainObject) Save() bool {
if do.Validate() {
fmt.Printf("Saving DomainObject with ID: %d\n", do.ID)
do.UpdateTimestamp()
return true
}
return false
}
func (do *DomainObject) Delete() bool {
fmt.Printf("Deleting DomainObject with ID: %d\n", do.ID)
return true
}
// Domain entities that embed the supertype
type User struct {
*DomainObject
Name string
Email string
}
func NewUser(id int, name, email string) *User {
return &User{
DomainObject: NewDomainObject(id),
Name: name,
Email: email,
}
}
func (u *User) Validate() bool {
return u.DomainObject.Validate() &&
u.Name != "" &&
u.Email != "" &&
len(u.Email) > 0
}
func (u *User) GetDisplayName() string {
return fmt.Sprintf("%s (%s)", u.Name, u.Email)
}
type Product struct {
*DomainObject
Name string
Price float64
}
func NewProduct(id int, name string, price float64) *Product {
return &Product{
DomainObject: NewDomainObject(id),
Name: name,
Price: price,
}
}
func (p *Product) Validate() bool {
return p.DomainObject.Validate() &&
p.Name != "" &&
p.Price > 0
}
func (p *Product) GetFormattedPrice() string {
return fmt.Sprintf("$%.2f", p.Price)
}
// Usage
func main() {
user := NewUser(1, "John Doe", "john@example.com")
product := NewProduct(1, "Laptop", 999.99)
fmt.Printf("User validation: %t\n", user.Validate())
fmt.Printf("User display: %s\n", user.GetDisplayName())
user.Save()
fmt.Printf("Product validation: %t\n", product.Validate())
fmt.Printf("Product price: %s\n", product.GetFormattedPrice())
product.Save()
}
Использована иллюстрация с сайта Мартина Фаулера.