首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >与抽象层具有不同签名的实现层的问题

与抽象层具有不同签名的实现层的问题
EN

Stack Overflow用户
提问于 2014-07-02 16:01:54
回答 2查看 77关注 0票数 0

我第三次遇到了这个设计问题,我有种感觉,有一个解决方案,我就是想不出来。我对我以前解决它的方式不满意,所以这里是你进来的地方。

假设我正在设计一个(C#)库,它不知道在哪个系统中使用它。所以我有以下课程:

代码语言:javascript
复制
public interface IAction
{
    void Execute();
}

public class Trigger
{
    private List<IAction> actions;

    public void CheckConditions()
    {
        if (AllConditionsMet())
        {
           ExecuteAllActions();
        }
    }

    private void ExecuteAllActions()
    {
        foreach (IAction action in actions)
        {
            action.Execute();
        }
    }

    protected abstract bool AllConditionsMet();
}

public class BigBulkySystem
{
    private List<Trigger> triggers;

    public void CheckAllTriggers()
    {
        foreach (Trigger trigger in triggers)
        {
            trigger.CheckConditions();
        }
    }
}

到目前一切尚好。我们在BigBulkySystem中有一个触发器列表,我们经常检查它们的条件。如果满足条件,则触发器负责执行其操作列表。

我现在面临的问题是,我希望实现一个专门为统一设计的层。在联合中,您使用协同线以便能够在继续执行方法之前等待几个帧。这正是我想要的Trigger.ExecuteAllActions方法。在移到下一个操作之前,我希望等待每个操作完成执行。为了做到这一点,我需要为触发器和IAction提供不同的签名。以下是团结的情况:

代码语言:javascript
复制
public interface IAction
{
    IEnumerator Execute();
}

public class Trigger
{
    private List<IAction> actions;

    public void CheckConditions()
    {
        if (AllConditionsMet())
        {
            StartCoroutine(ExecuteAllActions());
        }
     }

    public IEnunmerator ExecuteAllActions()
    {
        foreach (IAction action in actions)
        {
            yield return StartCoroutine(action.Execute());
        }
    }
 }

注意,现在有两个方法返回IEnumerator,而不是void,这是在Unity中定义协同线的方式。

下面是一个在3秒延迟后的5秒内将player对象从位置(0,0,0)移动到(1,1,1)的Unity示例:

代码语言:javascript
复制
public class MovePlayerAction : IAction
{
    public GameObject playerObject;

    public IEnumerator Execute()
    {
        yield return StartCoroutine(WaitFor(3)); // Wait 3 secs

        float animDuration = 5.0f;
        Vector3 initialPos = Vector3.zero;
        Vector3 finalPos = Vector3.one;
        float t = 0;
        while (t < animDuration)
        {
            playerObject.transform.position = Vector3.Lerp(initialPos, finalPos, t / animDuration); // Lerp between initial and final
            yield return null; // Wait a frame
            t += Time.deltaTime;
        }
    }

    private IEnumerator WaitFor(float secs)
    {
        float t = 0;
        while (t < secs)
        {
            yield return null; // Wait a single frame
            t += Time.deltaTime;
        }
    }
}

因此,我的问题是:是否有一种设计系统的方法,使非统一层不受IEnumerator的影响,但也让统一层使用它们?

我有一种感觉,那就是存在某种中间层,但即使如此,我还是不知道IAction如何能够同时拥有void Execute()IEnumerator Execute()

谢谢!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-07-02 17:30:08

当您使用一个具有与您需要的接口不同的类时,您需要适配器模式。在这方面有几个不同的地方,但对于您的情况,一个简单的变体如下所示:

代码语言:javascript
复制
public interface IAction
{
    void Execute();
}

public interface IUnityAction
{
    IEnumerator Execute();
}

public class UnityActionAdapter : IUnityAction
{
    private readonly IAction Action;

    public UnityActionAdapter(IAction action)
    {
        Action = action;
    }

    public IEnumerator Execute()
    {
        Action.Execute();
        yield break; //Or whatever you need to yield here
    }
}

更新

在您的情况下,您似乎正在适应另一个方向,因此:

代码语言:javascript
复制
public class UnityActionAdapter : IAction
{
    private readonly IUnityAction UnityAction;

    public UnityActionAdapter(IAction unityAction)
    {
        UnityAction = action;
    }

    public void Execute()
    {
        StartCoroutine(UnityAction.Execute()); //If I'm correctly understanding how you'd execute an action here
    }
}

然后让MovePlayerAction实现IUnityAction,而不是直接将MovePlayerAction传递到另一层,而是将其封装在UnityActionAdapter中,然后将其传递给其他层。

这是假设您实际上需要在统一层的接口中维护IEnumerator表单。您可以在不需要适配器的情况下通过执行以下操作来实现相同的目标:

代码语言:javascript
复制
public class MovePlayerAction : IAction
{
    public GameObject playerObject;

    private IEnumerator Coroutine()
    {
        yield return StartCoroutine(WaitFor(3)); // Wait 3 secs

        float animDuration = 5.0f;
        Vector3 initialPos = Vector3.zero;
        Vector3 finalPos = Vector3.one;
        float t = 0;
        while (t < animDuration)
        {
            playerObject.transform.position = Vector3.Lerp(initialPos, finalPos, t / animDuration); // Lerp between initial and final
            yield return null; // Wait a frame
            t += Time.deltaTime;
        }
    }

    public void Execute()
    {
        StartCoroutine(Coroutine());
    }

    private IEnumerator WaitFor(float secs)
    {
        float t = 0;
        while (t < secs)
        {
            yield return null; // Wait a single frame
            t += Time.deltaTime;
        }
    }
}
票数 1
EN

Stack Overflow用户

发布于 2014-07-02 16:15:26

如果您使用任务并行库呢?

在我看到的触发器示例代码中,我们可以实现它(考虑到您的非IEnumerator代码):

代码语言:javascript
复制
public class Trigger
    {
        private readonly List<Task> TaskActions = new List<Task>();

        public void AddAction(IAction action)
        {
            TaskActions.Add(new Task(()=>{action.Execute();})
        }

        public void CheckConditions()
        {
            if (AllConditionsMet())
            {
                ExecuteAllActions();
            }
        }

        private void ExecuteAllActions()
        {
            foreach(Task task in TaskActions)
            {
                task.Start();
            }
            Task.WaitAll(TaskActions);
        }

        protected abstract bool AllConditionsMet();
    }

问候

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24535883

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档