过于复杂的设计模式

问题描述

我正在尝试使用设计模式解决设计问题。现在我有了基础知识,我很确定我把它复杂化了很多。我似乎有多个空的接口,我可能可以用不同的设计做更少的事情。此外,我不确定该项目的未来开发人员是否会轻松解决这个问题。

我制作了类结构的模型。该示例简化为两种服务类型(参见 BaseAnimalService 扩展),在项目中还有更多。还有更多 BaseStrategy 实现。

首先,我想区分 CatService 或 DogService 的上下文。这是使用 BaseStrategy 类中的 Map 完成的,该类将 BaseAnimalService 作为值以启用 Cat/DogService 之间的多态性。基于在 Dog/CatStrategy 中实现的 BaseStrategy 的通用类型,使用了不同的 configurationMap,它依次根据条件的类型加载 Dog/CatService 的一个或另一个实现。 配置映射在 spring.xml 文件中定义。

由于 Dog/CatService 都实现了一个额外的接口,参见。 SomeOtherCat/DogService 是我设计的外部,Dog/CatService 也有空接口。 SomeOtherCatService 和 SomeOtherDogService 不相关且不可编辑,所以我不能多态地使用它们,这就是 Base/Cat/DogService 接口的原因。

我想过将 BaseStrategy 设为一个 StrategyFactory,它返回一个 Cat/DogStrategy,它反过来检查 BaseAnimalService 使用的标准的类型。但由于这两种策略对其策略使用相同的逻辑,这意味着我必须创建另一个基类。

你怎么看?关于这个问题的更好设计有什么建议吗?或者对当前的有什么改进?

class BaseStrategy<T extends BaseAnimalService> {

    private ContextService contextService;
    private Map<String,BaseAnimalService> configurationMap;

    T getService() {
        return configurationMap.get(contextService.getCurrentContext());
    }
}

interface BaseAnimalService {
    //empty
}

interface DogService extends BaseAnimalService {
    //empty
}

interface CatService extends BaseAnimalService {
    //empty
}

class DogStrategy extends BaseStrategy<DogService> {
    //empty
}

class CatStrategy extends BaseStrategy<CatService> {
    //empty
}

class BritishShortHairserviceImpl implements CatService,SomeOtherCatService {
    @Override //source: SomeOtherCatService,same for other implementations below
    void pur() {
        //pur
    }
}

class LionServiceImpl implements CatService,SomeOtherCatService {
    @Override
    void pur() {
        //pur
    }
}

class PitBullServiceImpl implements DogService,SomeOtherDogService {
    @Override
    void wagTail() {
        //wag tail
    }
}

class ChihuahuaServiceImpl implements DogService,SomeOtherDogService {
    @Override
    void wagTail() {
        //wag tail
    }
}

class CatPerson {
    private BaseStrategy<CatService> catStrategy;

    void pet() {
        catStrategy.getService().pur();
    }
}

class DogPerson {
    private BaseStrategy<DogService> dogStrategy;

    void Feed() {
        dogStrategy.getService().wagTail();
    }
}

相关 spring.xml 片段:


<bean id="baseStrategy" abstract="true"
          class="com.animals.services.BaseStrategy">
        <property name="contextService" ref="contextService"/>
    </bean>

<bean id="catServiceStrategy"
          class="com.animals.services.CatStrategyImpl"
          parent="baseStrategy">
        <property name="strategyConfigurationMap">
            <map>
                <entry key="CONTEXT1" value-ref="britishShortHairservice"/>
                <entry key="CONTEXT2" value-ref="lionService"/>
            </map>
        </property>
    </bean>

<bean id="dogServiceStrategy"
          class="com.animals.services.DogStrategyImpl"
          parent="baseStrategy">
        <property name="strategyConfigurationMap">
            <map>
                <entry key="CONTEXT1" value-ref="pitbullService"/>
                <entry key="CONTEXT2" value-ref="chihuahuaService"/>
            </map>
        </property>
    </bean>

解决方法

我不熟悉 Spring 或其上下文服务模型,所以我是从一般的、独立于语言的 OOP 角度来解决这个问题的。

在我看来,您需要考虑通过构造函数(依赖注入)传递配置而不是基于映射进行切换的方式。您需要更多的“has a”关系(组合)和更少的“is a”关系(继承)。

AnimalService 可以将动物对象作为构造函数的参数。我们可以说 AnimalFeedbackBehavior 必须包含 positiveFeedback()neutralFeedback()negativeFeedback() 的方法——但是这些方法的实现方式因动物而异。 Catpurr() 响应积极的互动,但 DogwagTail()

AnimalOwner 可以feed() 任何动物并触发AnimalFeedbackBehavior.positiveFeedback()AnimalOwner 不需要知道该行为在幕后做了什么。它甚至不需要知道它有什么种类的动物。它需要知道的只是这个方法存在。

interface AnimalFeedbackBehavior {
    positiveFeedback(): void;
    neutralFeedback(): void;
    negativeFeedback(): void;
}

class AnimalOwner {
    private animal: AnimalFeedbackBehavior;

    // pass animal instance to the constructor
    constructor( animal: AnimalFeedbackBehavior) {
        this.animal = animal;
    }

    // trigger positive feedback when feeding
    feed() {
        this.animal.positiveFeedback();
    }
}

class Cat implements AnimalFeedbackBehavior {
    purr() {
        //do something
    }

    positiveFeedback() {
        this.purr();
    }

    /* ... rest of class ... */
}

Typescript Playground Link

这里我们假设 feed 总是积极的互动。但是,如果我们希望不同的动物对相同的互动有不同的反应呢? chase() 对于 Dog 可能是正数,但对于 Cat 可能是负数。一种天真的方法是根据地图切换反馈。但是理想的设计允许最大限度地抽象,其中 AnimalOwner 不需要了解任何关于动物类型的信息。

让我们尝试一个完全不同的设置。

如果您正在处理一小组行为​​,我们可以要求动物对每个行为做出反应,而不是积极/中性/消极。

interface AnimalBehavior {
    feedResponse(): void;
    chaseResponse(): void;
}

但这很快就会变得笨拙。我们可以使用 respond 方法定义一个动物,该方法响应某种通用操作对象。在实现中,它可以对动作做出响应,也可以直接忽略它。

这种设置还使多个覆盖行为的组合更加直观,因为我们可以遍历一系列 respond 函数,直到有人处理它为止。我们想知道是否有响应,因此我们需要从 response 函数返回一些内容。如果它基本上是 void,那么我们可以返回一个 boolean 标志,如果它有响应,它就是 true。如果响应应该返回一个值,那么您将返回该值或 undefined

interface Action {
    type: string;
}

// we may want to attach some sort of data
interface ActionWithData<T> extends Action {
    type: string;
    data: T;
}

interface AnimalBehavior {
    respond( action: Action ): string | undefined;
}

class Animal implements AnimalBehavior {
    // an animal has an array of behavior responders
    // as written,the earlier behaviors in the array override later ones
    private behaviors: AnimalBehavior[];

    // can instantiate an animal with multiple behaviors
    constructor( behaviors: AnimalBehavior[] = [] ) {
        this.behaviors = behaviors;
    }

    // can also add behaviors after the fact
    public addOverride( behavior: AnimalBehavior ) {
        this.behaviors = [behavior,...this.behaviors];
    }

    // loop through behaviors until one responds
    public respond (action: Action): string | undefined {
        for ( let element of this.behaviors ) {
            // could be a response or might be undefined
            const response = element.respond(action);
            if ( response ) {
                return response;
            }
        }
        // could do something here if no behaviors responded
        return undefined;
    }
}

class AnimalOwner {
    private animal: AnimalBehavior;

    // pass animal instance to the constructor
    constructor( animal: AnimalBehavior) {
        this.animal = animal;
    }

    // animal can respond to the feed action,or not
    feed(): string | undefined {
        return this.animal.respond({type: 'feed'});
    }

    chase(): string | undefined {
        return this.animal.respond({ type: 'chase' });
    }
}

这些实现目前感觉很草率。现在他们都没有使用 this,所以使用 class 毫无意义。但只是给你一个想法:

class DogBehavior implements AnimalBehavior {
    respond(action: Action): string | undefined {
        switch (action.type) {
            case 'feed':
                return "Wag Tail";
            case 'chase':

                return "Run Around";
            default:
                return undefined;
        }
    }
}

class PuppyBehavior implements AnimalBehavior {
    respond(action: Action): string | undefined {
        switch (action.type) {
            case 'feed':
                return "Jump";
            default:
                return undefined;
        }
    }
}

class ChihuahuaBehavior implements AnimalBehavior {
    respond(action: Action): string | undefined {
        switch (action.type) {
            case 'feed':
                return "Yip";
            default:
                return undefined;
        }
    }
}

Animal 组合和单个行为都实现了 AnimalBehavior,因此 AnimalOwner 可以直接采用 DogBehavior,也可以采用 Animal 组成DogBehavior 和其他一些行为。

const owner1 = new AnimalOwner(new DogBehavior());
let res = owner1.feed(); // is "Wag Tail"

顺序很重要。如果我们有一只吉娃娃小狗,我们需要决定 ChihuahuaBehavior 是否覆盖 PuppyBehavior,反之亦然。

// prioritizes puppy
const owner2 = new AnimalOwner(new Animal([new PuppyBehavior(),new ChihuahuaBehavior(),new DogBehavior()]));
res = owner2.feed(); // is "Jump" from PuppyBehavior
res = owner2.chase(); // is "Run Around" from DogBehavior because not overwritten

// prioritizes chihuahua
const owner3 = new AnimalOwner(new Animal([new ChihuahuaBehavior(),new PuppyBehavior(),new DogBehavior()]));
res = owner3.feed(); // is "Yip" from ChihuahuaBehavior

Typescript Playground Link