面向对象编程问题 - 状态设计模式

时间:2013-05-04 05:22:37

标签: oop design-patterns state strategy-pattern

我花了最后一天试图找出最符合我特定情况的模式,而且我一直在状态模式和状态之间折腾。战略模式。当我在互联网上阅读这些例子时,它非常有意义......但这是尝试将其应用于您自己的问题的另一项技能。我将描述我的情景和我面临的问题,希望有人可以指出我正确的方向。

问题:我有一个具有不同同步状态的基础对象:即最新,旧版,从未发布,未发布等。现在,根据对象在行为中的状态是不同的,例如,您无法获取最新版本从未发布过的基础对象。在这一点上,似乎State设计模式最适合......所以我已经实现了它,现在每个州都有CanGetLatestVersion,GetLatestVersion,CanPublish,Publish等方法。

此时一切似乎都很好。但是,假设您有10个不同的子对象派生自基类...我的解决方案被破坏,因为当为每个状态执行“publish”方法时,它需要子对象中的属性来实际执行操作但是每个状态只有对基础对象的引用。我花了一些时间创建一个示例项目来说明我在C#中的问题。

public class BaseDocument
{
    private IDocumentState _documentState;

    public BaseDocument(IDocumentState documentState)
    {
        _documentState = documentState; 
    }

    public bool CanGetLatestVersion()
    {
        return _documentState.CanGetLatestVersion(this); 
    }

    public void GetLatestVersion()
    {
        if(CanGetLatestVersion())
            _documentState.CanGetLatestVersion(this); 
    }

    public bool CanPublish()
    {
        return _documentState.CanPublish(this);
    }

    public void Publish()
    {
        if (CanPublish())
            _documentState.Publish(this);
    }

    internal void Change(IDocumentState documentState)
    {
        _documentState = documentState; 
    }
}

public class DocumentSubtype1 : BaseDocument
{
    public string NeedThisData { get; set; }
}

public class DocumentSubtype2 : BaseDocument 
{
    public string NeedThisData1 { get; set; }
    public string NeedThisData2 { get; set; }
}

public interface IDocumentState
{
    bool CanGetLatestVersion(BaseDocument baseDocument);
    void GetLatestVersion(BaseDocument baseDocument);
    bool CanPublish(BaseDocument baseDocument);
    bool Publish(BaseDocument baseDocument);
    SynchronizationStatus Status { get; set; }     
}

public class LatestState : IDocumentState
{
    public bool CanGetLatestVersion(BaseDocument baseDocument)
    {
        return false; 
    }

    public void GetLatestVersion(BaseDocument baseDocument)
    {
        throw new Exception(); 
    }

    public bool CanPublish(BaseDocument baseDocument)
    {
        return true; 
    }

    public bool Publish(BaseDocument baseDocument)
    {
        //ISSUE HERE... I need to access the properties in the the DocumentSubtype1 or DocumentSubType2 class. 
    }

    public SynchronizationStatus Status
    {
        get
        {
            return SynchronizationStatus.LatestState; 
        }
    }
}

public enum SynchronizationStatus
{
    NeverPublishedState, 
    LatestState,
    OldState,
    UnpublishedChangesState,
    NoSynchronizationState
}

然后我考虑为每个子对象实现状态......哪个可以工作但是我需要创建50个类,即(10个孩子x5个不同的状态),这看起来绝对疯狂...因此我为什么在这里!

非常感谢任何帮助。如果它令人困惑,请让我知道所以我可以澄清!

干杯

2 个答案:

答案 0 :(得分:0)

让我们重新考虑一下。

1)你有一个本地的“处理”,对于一些你并不真正拥有的数据。 (其中一些是在其他地方存储或发布的)。

2)也许Handle,就是我们之前所说的'State' - 一个简单的通用API,没有实现细节。

3)而不是'CanPublish','GetLatestVersion'从BaseDocument委托给State--听起来像Handle应该委托给特定的DocumentStorage实现。

4)当表示外部状态或存储位置时,使用单独的对象非常适合封装New / Existent / Deletion状态&标识符,在该存储位置。

5)我不确定'版本'是否属于'已发布位置';或者如果它们是两个独立的存储位置。我们的句柄需要每个独立位置的“存储状态”表示,它将存储到/来自。

例如:

Handle
  - has 1 LocalCopy with states (LOADED, NOT_LOADED)
  - has 1 PublicationLocation with Remote URL and states (NEW, EXIST, UPDATE, DELETE)

Handle.getVersions() then delegates to PublicationLocation.
Handle.getCurrent() loads a LocalCopy (cached), from PublicationLocation.
Handle.setCurrent() sets a LocalCopy and sets Publication state to UPDATE.
  (or executes the update immediately, whichever.)

远程存储位置/传输可以针对不同的访问方式进行子类型化,或者LocalCopy / Document可以针对不同类型的内容进行子类型化。

这,我很确定,是更正确的解决方案。


[以前]保持'State'与你的'Document'对象有些分开(我们称之为Document,因为我们需要将它称为某些东西 - 而你没有指定。)

从BaseDocument构建你的层次结构,拥有一个BaseDocument.State成员,并通过对其Document实例的引用创建State对象 - 这样他们就可以访问&可以处理细节。

本质:

  • BaseDocument< - friend - >状态
  • 文档子类型继承自BaseDocument。
  • 受保护的方法&文件层次结构中的成员,使国家能够做任何需要的事情。

希望这有帮助。

答案 1 :(得分:0)

许多设计模式可用于此类架构问题。遗憾的是,您没有举例说明您如何进行发布。但是,我将陈述一些好的设计:

  1. 将其他参数放入基础文档并进行制作 空。如果未在文档中使用,则为null。否则,它 有价值。你不需要继承。

  2. 不要将Publish方法放到DocumentState,放入 而是BaseDocument。从逻辑上讲,Publish方法必须是其中的一部分 BaseDocument而不是DocumentState。

  3. 让其他服务类来处理发布(发布者 服务)。您可以使用抽象工厂模式来实现它。这个 方式,您需要创建1:1文档:publisher对象。它可能是 很多,但您可以自由修改每个文档的发布者。

    public interface IPublisher<T> where T : BaseDocument
    {
        bool Publish(T document);
    }
    
    public interface IPublisherFactory
    {
        bool Publish(BaseDocument document);
    }
    
    public class PublisherFactory : IPublisherFactory
    {
        public PublisherFactory(
            IPublisher<BaseDocument> basePublisher
            , IPublisher<SubDocument1> sub1Publisher)
        {
            this.sub1Publisher = sub1Publisher;
            this.basePublisher = basePublisher;
        }
        IPublisher<BaseDocument> basePublisher;
        IPublisher<SubDocument1> sub1Publisher;
    
        public bool Publish(BaseDocument document)
        {
            if(document is SubDocument1)
            {
                return sub1Publisher.Publish((SubDocument1)document);
            }
            else if (document is BaseDocument)
            {
                return basePublisher.Publish(document);
            }
            return false;
        }
    }
    
    public class LatestState : IDocumentState
    {
        public LatestState(IPublisherFactory factory)
        {
            this.factory = factory;
        }
        IPublisherFactory factory;
    
        public bool Publish(BaseDocument baseDocument)
        {
            factory.Publish(baseDocument);
        }
    }
    
  4. 使用Composition over inheritance。您为每个状态设计每个接口,然后在文档中组合它。总之,您可以拥有5个CanGetLatestVersion和其他合成类,但是10个发布者合成类。

  5. 更高级,并且基于您使用的存储库,也许您可​​以使用Visitor pattern。这样,您就可以自由地修改每种发布方法。它与我的第3点类似,除了它在一个类中声明。例如:

    public class BaseDocument
    {
    
    }
    public class SubDocument1 : BaseDocument
    {
    
    }
    
    public class DocumentPublisher
    {
        public void Publish(BaseDocument document)
        {
    
        }
        public void Publish(SubDocument1 document)
        {
            // do the prerequisite
            Publish((BaseDocument)document);
            // do the postrequisite
        }
    }
    
  6. 可能还有其他设计,但这取决于您访问存储库的方式。