分类 设计模式 下的文章

适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将不兼容的接口转换为另一个接口,以满足客户端的需求。在 Golang 中实现适配器模式可以使用接口和组合来实现。

下面是一个使用 Golang 实现适配器模式的示例代码:

package main

import "fmt"

type Target interface {
    Request()
}

type Adaptee struct {}

func (a *Adaptee) SpecificRequest() {
    fmt.Println("Adaptee.SpecificRequest()")
}

type Adapter struct {
    adaptee *Adaptee
}

func (a *Adapter) Request() {
    a.adaptee.SpecificRequest()
}

func main() {
    adaptee := &Adaptee{}
    adapter := &Adapter{adaptee}

    adapter.Request()
}

在上面的代码中,我们定义了一个 Target 接口,它包含一个 Request 方法,用于执行请求。我们还定义了一个 Adaptee 结构体,它包含一个 SpecificRequest 方法,用于执行特定的请求。

接着,我们定义了一个 Adapter 结构体,它包含一个指向 Adaptee 的指针,并实现了 Target 接口的 Request 方法。该方法会调用 Adaptee 的 SpecificRequest 方法,将不兼容的接口转换为 Target 接口。

最后,在 main 函数中,我们创建了一个 Adaptee 实例,并将其传递给 Adapter 实例。我们调用 adapter.Request() 方法,实际上是调用了 Adaptee.SpecificRequest() 方法,但由于适配器的存在,客户端无需直接调用 Adaptee 的方法,从而实现了接口的兼容。

装饰模式(Decorator Pattern)是一种结构型设计模式,它允许动态地给一个对象添加新的功能,而无需修改其源代码。在 Golang 中实现装饰模式可以使用接口和组合来实现。

下面是一个使用 Golang 实现装饰模式的示例代码:

package main

import "fmt"

type Component interface {
    Operation()
}

type ConcreteComponent struct{}

func (c *ConcreteComponent) Operation() {
    fmt.Println("ConcreteComponent.Operation()")
}

type Decorator interface {
    Component
}

type ConcreteDecoratorA struct {
    component Component
}

func (d *ConcreteDecoratorA) Operation() {
    d.component.Operation()
    fmt.Println("ConcreteDecoratorA.Operation()")
}

type ConcreteDecoratorB struct {
    component Component
}

func (d *ConcreteDecoratorB) Operation() {
    d.component.Operation()
    fmt.Println("ConcreteDecoratorB.Operation()")
}

func main() {
    component := &ConcreteComponent{}

    decoratorA := &ConcreteDecoratorA{component}
    decoratorB := &ConcreteDecoratorB{decoratorA}

    decoratorB.Operation()
}

在上面的代码中,我们定义了一个 Component 接口,它包含一个 Operation 方法,用于执行操作。我们还定义了一个 ConcreteComponent 结构体,它实现了 Component 接口的 Operation 方法。

接着,我们定义了一个 Decorator 接口,它继承了 Component 接口,并定义了两个具体的装饰器类:ConcreteDecoratorA 和 ConcreteDecoratorB。这两个装饰器类都包含一个 component 成员变量,用于存储被装饰的组件对象。它们也都实现了 Operation 方法,该方法会先调用被装饰的组件对象的 Operation 方法,然后再执行自己的操作。

最后,在 main 函数中,我们创建了一个 ConcreteComponent 实例,并分别用 ConcreteDecoratorA 和 ConcreteDecoratorB 对其进行装饰。由于 ConcreteDecoratorA 和 ConcreteDecoratorB 都实现了 Component 接口,因此它们可以像 ConcreteComponent 一样被传递和使用。最终,我们调用 decoratorB.Operation() 方法,会先调用 ConcreteComponent.Operation() 方法,然后依次执行 ConcreteDecoratorA.Operation() 和 ConcreteDecoratorB.Operation() 方法。通过这种方式,我们可以动态地给一个对象添加新的功能,而无需修改其源代码。

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生改变时,其所有依赖对象都会自动得到通知并更新。在 Golang 中实现观察者模式可以使用接口和函数来实现。

下面是一个使用 Golang 实现观察者模式的示例代码:

package main

import (
    "fmt"
    "sync"
)

type Observer interface {
    Update(msg string)
}

type Subject struct {
    observers []Observer
    mutex     sync.Mutex
}

func (s *Subject) RegisterObserver(observer Observer) {
    s.mutex.Lock()
    defer s.mutex.Unlock()
    s.observers = append(s.observers, observer)
}

func (s *Subject) RemoveObserver(observer Observer) {
    s.mutex.Lock()
    defer s.mutex.Unlock()
    for i, o := range s.observers {
        if o == observer {
            s.observers = append(s.observers[:i], s.observers[i+1:]...)
            break
        }
    }
}

func (s *Subject) NotifyObservers(msg string) {
    s.mutex.Lock()
    defer s.mutex.Unlock()
    for _, observer := range s.observers {
        observer.Update(msg)
    }
}

type ConcreteObserver1 struct{}

func (o *ConcreteObserver1) Update(msg string) {
    fmt.Println("ConcreteObserver1 received message:", msg)
}

type ConcreteObserver2 struct{}

func (o *ConcreteObserver2) Update(msg string) {
    fmt.Println("ConcreteObserver2 received message:", msg)
}

func main() {
    subject := &Subject{}
    observer1 := &ConcreteObserver1{}
    observer2 := &ConcreteObserver2{}

    subject.RegisterObserver(observer1)
    subject.RegisterObserver(observer2)

    subject.NotifyObservers("Hello World!")

    subject.RemoveObserver(observer2)

    subject.NotifyObservers("Goodbye World!")
}

在上面的代码中,我们定义了一个 Observer 接口,它包含一个 Update 方法,用于接收通知。我们还定义了一个 Subject 结构体,它包含一个 observers 切片用于存储观察者对象,并使用 sync.Mutex 来保证线程安全。

在 Subject 结构体中,我们定义了三个方法:RegisterObserver、RemoveObserver 和 NotifyObservers。RegisterObserver 方法用于注册观察者对象,RemoveObserver 方法用于移除观察者对象,NotifyObservers 方法用于通知所有观察者对象。

接下来,我们定义了两个具体的观察者类:ConcreteObserver1 和 ConcreteObserver2,它们实现了 Observer 接口的 Update 方法,用于接收通知并输出消息。

最后,在 main 函数中,我们创建了一个 Subject 实例和两个观察者实例,并分别调用 RegisterObserver 方法来注册观察者对象。然后,我们调用 NotifyObservers 方法来通知所有观察者对象,并输出相应的消息。接着,我们调用 RemoveObserver 方法来移除一个观察者对象,再次调用 NotifyObservers 方法来

工厂模式(Factory Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但将具体的对象创建延迟到子类中。在 Golang 中实现工厂模式通常使用函数来实现。

下面是一个使用 Golang 实现工厂模式的示例代码:

package main

import "fmt"

type Product interface {
    GetName() string
}

type ProductA struct{}

func (p *ProductA) GetName() string {
    return "Product A"
}

type ProductB struct{}

func (p *ProductB) GetName() string {
    return "Product B"
}

func CreateProduct(name string) Product {
    switch name {
    case "A":
        return &ProductA{}
    case "B":
        return &ProductB{}
    default:
        return nil
    }
}

func main() {
    productA := CreateProduct("A")
    fmt.Println(productA.GetName())

    productB := CreateProduct("B")
    fmt.Println(productB.GetName())
}

在上面的代码中,我们定义了一个 Product 接口,它包含一个 GetName 方法,用于获取产品名称。接下来,我们定义了两个具体的产品类:ProductA 和 ProductB,它们实现了 Product 接口的 GetName 方法。

我们还定义了一个 CreateProduct 函数,该函数根据传入的产品名称来创建具体的产品实例。在 CreateProduct 函数中,我们使用 switch 语句根据产品名称来创建相应的产品实例。

最后,在 main 函数中,我们分别使用 CreateProduct 函数来创建 ProductA 和 ProductB 实例,并输出它们的名称。可以看到,我们只需要传入相应的产品名称,就可以创建相应的产品实例,而无需关心具体的产品实现细节。这正是工厂模式的优势所在,它将对象的创建与使用代码分离开来,使得客户端代码更加简洁和易于维护。

单例模式(Singleton Pattern)是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点。在 Golang 中实现单例模式可以使用 sync 包提供的 Once 和 Mutex 来实现。

下面是一个使用 Golang 实现单例模式的示例代码:

package main

import (
    "fmt"
    "sync"
)

type Singleton struct {
    data string
}

var instance *Singleton
var once sync.Once

func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{"Hello World!"}
    })
    return instance
}

func main() {
    s1 := GetInstance()
    s2 := GetInstance()

    if s1 == s2 {
        fmt.Println("Same instance")
    } else {
        fmt.Println("Different instance")
    }
}

在上面的代码中,我们定义了一个 Singleton 结构体,该结构体包含一个 data 字段。我们还定义了一个全局变量 instance,用于存储 Singleton 的唯一实例。

在 GetInstance 函数中,我们使用 sync.Once 来保证 instance 只被初始化一次。sync.Once 会在第一次调用 Do 方法时执行指定的函数,后续调用将被忽略。因此,只要我们通过 GetInstance 方法获取 instance,就可以保证 Singleton 的唯一性。

最后,在 main 函数中,我们分别使用 GetInstance 方法获取 Singleton 实例,并比较它们的地址是否相同。如果相同,则说明它们是同一个实例;否则,它们是不同的实例。

策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,将每个算法封装起来,并且使它们之间可以互相替换。该模式使得算法可以独立于使用它的客户端而变化。

下面是一个使用 Golang 实现策略模式的示例代码:

package main

import "fmt"

type PaymentStrategy interface {
    Pay(amount float64) error
}

type CreditCardStrategy struct{}

func (cc *CreditCardStrategy) Pay(amount float64) error {
    fmt.Printf("Paid %0.2f using Credit Card.\n", amount)
    return nil
}

type PayPalStrategy struct{}

func (pp *PayPalStrategy) Pay(amount float64) error {
    fmt.Printf("Paid %0.2f using PayPal.\n", amount)
    return nil
}

type PaymentContext struct {
    strategy PaymentStrategy
}

func (pc *PaymentContext) SetPaymentStrategy(strategy PaymentStrategy) {
    pc.strategy = strategy
}

func (pc *PaymentContext) MakePayment(amount float64) error {
    return pc.strategy.Pay(amount)
}

func main() {
    creditCardStrategy := &CreditCardStrategy{}
    payPalStrategy := &PayPalStrategy{}

    paymentContext := &PaymentContext{}

    // 使用信用卡支付
    paymentContext.SetPaymentStrategy(creditCardStrategy)
    paymentContext.MakePayment(100.00)

    // 使用 PayPal 支付
    paymentContext.SetPaymentStrategy(payPalStrategy)
    paymentContext.MakePayment(200.00)
}

在上面的代码中,我们定义了一个 PaymentStrategy 接口,该接口包含一个 Pay 方法,用于支付指定金额。接下来,我们定义了两个具体的支付策略类:CreditCardStrategy 和 PayPalStrategy,它们实现了 PaymentStrategy 接口的 Pay 方法。

我们还定义了一个 PaymentContext 结构体,该结构体包含一个 strategy 字段,用于存储当前的支付策略。PaymentContext 结构体还定义了两个方法:SetPaymentStrategy 用于设置支付策略,MakePayment 用于执行支付操作。

最后,在 main 函数中,我们创建了一个 CreditCardStrategy 和 PayPalStrategy 实例,并分别使用它们来进行支付。可以看到,在 MakePayment 方法中,我们只需要调用 strategy.Pay(amount) 来执行支付操作,而无需关心具体的支付策略是什么。这正是策略模式的优势所在,它将算法的实现细节与客户端代码隔离开来,使得客户端代码更加简洁和易于维护。

观察者模式(Observer),当一个对象的状态发生改变时,依赖他的对象会全部收到通知,并自动更新。

场景:一个事件发生后,要执行一连串更新操作.传统的编程方式,就是在事件的代码之后直接加入处理逻辑,当更新得逻辑增多之后,代码会变得难以维护.这种方式是耦合的,侵入式的,增加新的逻辑需要改变事件主题的代码
观察者模式实现了低耦合,非侵入式的通知与更新机制。


/**
 * 事件产生类
 * Class EventGenerator
 */
abstract class EventGenerator
{
    private $ObServers = [];

    //增加观察者
    public function add(ObServer $ObServer)
    {
        $this->ObServers[] = $ObServer;
    }

    //事件通知
    public function notify()
    {
        foreach ($this->ObServers as $ObServer) {
            $ObServer->update();
        }
    }

}

/**
 * 观察者接口类
 * Interface ObServer
 */
interface ObServer
{
    public function update($event_info = null);
}

/**
 * 观察者1
 */
class ObServer1 implements ObServer
{
    public function update($event_info = null)
    {
        echo "观察者1 收到执行通知 执行完毕!\n";
    }
}

/**
 * 观察者1
 */
class ObServer2 implements ObServer
{
    public function update($event_info = null)
    {
        echo "观察者2 收到执行通知 执行完毕!\n";
    }
}

/**
 * 事件
 * Class Event
 */
class Event extends EventGenerator
{
    /**
     * 触发事件
     */
    public function trigger()
    {
        //通知观察者
        $this->notify();
    }
}

//创建一个事件
$event = new Event();
//为事件增加旁观者
$event->add(new ObServer1());
$event->add(new ObServer2());
//执行事件 通知旁观者
$event->trigger();

外观(Facade)模式
当使用子系统的代码时,你也许会发现自己过于深入地调用子系统的逻辑代码。如果子系统代码总是在不断变化,而你的代码却又在许多不同地方与子系统代码交互,那么随着子系统的发展,你也许会发现维护代码变得非常困难。

在项目中集成复杂的第三方代码,或在系统中逐渐形成大量仅在系统自身内部有用的代码,在这些情况下,你总可以应用外观模式,为复杂的系统创建一个简单、清晰的接口。

假设有下面一段很乱的代码,其功能是从文件中获取log信息并将它转换为对象。

用 laravel 框架举例
Laravel中我们常用到的Route、Redis、Auth这些Facade就是外观模式的具体实现, 在Laravel中设计了多个外观类,每个外观类继承自统一的抽象外观类,在抽象外观类里提供了通过外观类访问其背后子系统的基础方法。


class Client

{

    public function main()

    {

        (new Facade)->operation();

    }

}

 

class Facade

{

    private $systemA;

    private $systemB;

     

    public function __construct()

    {

        $this->systemA = new SystemA;

        $this->systemB = new SystemB;

    }

     

    public function operation()

    {

        $this->systemA->operationA();

        $this->systemB->operationB();

    }

}

 

class SystemA

{

    public function operationA()

    {

        //

    }

}

 

class SystemB

{

    public function operationB()

    {

        //

    }

}

模式分析
根据“单一职责原则”,在软件中将一个系统划分为若干个子系统有利于降低整个系统的复杂性,一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小,而达到该目标的途径之一就是引入一个外观对象,它为子系统的访问提供了一个简单而单一的入口。 -外观模式也是“迪米特法则”的体现,通过引入一个新的外观类可以降低原有系统的复杂度,同时降低客户类与子系统类的耦合度。 - 外观模式要求一个子系统的外部与其内部的通信通过一个统一的外观对象进行,外观类将客户端与子系统的内部复杂性分隔开,使得客户端只需要与外观对象打交道,而不需要与子系统内部的很多对象打交道。 -外观模式的目的在于降低系统的复杂程度。 -外观模式从很大程度上提高了客户端使用的便捷性,使得客户端无须关心子系统的工作细节,通过外观角色即可调用相关功能。

原文:https://www.php.cn/phpkj/laravel/407270.html

策略模式就类似于c++中的多态,同一行为有不同的实现。

在业务上会有这个场景,比如获取列表,不同的数据源获取不同的列表。那么可能会有这个实现

$upstreamType = $params['upstream_type'];
    $list = [];
    switch ($upstreamType) {
        case 1:
            $list = $this->list1**(**);
            break;
        case 4:
            $list = $this->list4**(**);
            break;
        default:
    }

- 阅读剩余部分 -