2015-07-10 45 views
0

我有一个序列化任务,其中我的对象模型的不同部分知道如何发出自己的字符串段,但是我希望可以使用.Append针对常见的StringBuilder对象发出它们。根据理论,我们不应该把我们的私人对象传递给别人滥用,下面是一个包装StringBuilder的类,使它成为“仅用于追加”用于我的场景。绕过一个“AppendOnlyStringBuilder”

public sealed class AppendOnlyStringBuilder { 
    private readonly StringBuilder _stringBuilder; 

    public AppendOnlyStringBuilder(StringBuilder stringBuilder) 
    { 
     _stringBuilder = stringBuilder; 
    } 

    public AppendOnlyStringBuilder Append<T>(T value) 
    { 
     _stringBuilder.Append(value); 
     return this; 
    } 
} 

如果我的模型的不同块的内部代码,现在所有类似于此:

// Chunk1 class 
public override string ToString() { 
    StringBuilder sb = new StringBuilder(); 
    sb 
     .Append(_prop1.Value) 
     .Append(_prop2.Value) 
     .Append(_prop3.Value); 
    return sb.ToString(); 
} 

而且这些ToString方法是从主程序的序列化方法被称为像这样:

// MainObject class 
Chunk1 _chunk1; 
Chunk2 _chunk2; 
Chunk3 _chunk3; 

public override string ToString() { 
    StringBuilder sb = new StringBuilder(); 
    sb 
     .Append(_chunk1.ToString()) // ToString may be unnecessary here 
     .Append(_chunk2.ToString()) 
     .Append(_chunk3.ToString()); 
    return sb.ToString(); 
} 

如何优雅地切换到单个AppendOnlyStringBuilder以便在所有这些块类中使用,而不是在内部创建新的StringBuilderToString

我想它要使用这样的事:

// MainObject class 
public override string ToString() { 
    StringBuilder sb = new StringBuilder(); 
    AppendOnlyStringBuilder aosb = new AppendOnlyStringBuilder(sb); 
    aosb 
     .Append(_chunk1) 
     .Append(_chunk2) 
     .Append(_chunk3); 
    return sb.ToString(); 
} 

扩展方法是一种自然的方式来得到这个语法,但我运行到由于扩展方法的问题需要被静态,因此无法访问Chunk类的私有部分。我想在大块类中保留相同的ToString,这样他们仍然可以按照正常的“序列化我”的方式进行操作,但是我也希望它们能够选择附加到我的共同AppendOnlyStringBuilder

我想我能做到这一点:

_chunk1.AppendTo(aosb); 
_chunk2.AppendTo(aosb); 
_chunk3.AppendTo(aosb); 

但一些事困扰我 - 我倒是喜欢用与AppendOnlyStringBuilder对象开始在以前我的例子能说一口流利的接口。

+0

各种'Chunk'类将只有一个'AppendOnlyStringBuilder'访问。他们怎么能对主对象实例化并保持私有的底层对象做任何事情? – ErikE

+0

我会设置一个系统,让对象序列化到TextWriter(但如果您愿意,可以继续使用StringBuilder)。然后ToString覆盖只是创建一个新的TW(或SB),调用另一个方法并返回结果。 serialize方法可以是抽象的,并且ToString重写只能在基类中定义一次。这失去了方法链,但我不认为这是一个很大的损失。您可能可以使用访问者模式保留它。我不确定这项工作是否值得。 – phoog

+0

@phoog只要我们不结束重复字符串连接的问题,'TextWriter'也可以。你能给出一些示例代码来概述你的想法吗? – ErikE

回答

1

好吧,这里有云:

using System.IO; 

abstract class BaseClass 
{ 
    protected abstract void WriteToTextWriter(TextWriter textWriter); 

    public void SerializeTo(TextWriter textWriter) 
    { 
     WriteToTextWriter(textWriter); 
    } 

    public sealed override string ToString() 
    { 
     var writer = new StringWriter(); 
     SerializeTo(writer); 
     return writer.ToString(); 
    } 
} 

abstract class ChunkBase : BaseClass 
{ 
    private readonly string _index; 

    protected ChunkBase(string index) 
    { 
     _index = index; 
    } 

    protected sealed override void WriteToTextWriter(TextWriter textWriter) 
    { 
     textWriter.Write("Chunk"); 
     textWriter.Write(_index); 
    } 
} 

class Chunk1 : ChunkBase { public Chunk1() : base("1") { } } 
class Chunk2 : ChunkBase { public Chunk2() : base("2") { } } 
class Chunk3 : ChunkBase { public Chunk3() : base("3") { } } 

class ClassWithChunks : BaseClass 
{ 
    private readonly Chunk1 _chunk1 = new Chunk1(); 
    private readonly Chunk2 _chunk2 = new Chunk2(); 
    private readonly Chunk3 _chunk3 = new Chunk3(); 

    protected override void WriteToTextWriter(TextWriter textWriter) 
    { 
     _chunk1.SerializeTo(textWriter); 
     _chunk2.SerializeTo(textWriter); 
     _chunk3.SerializeTo(textWriter); 
    } 
} 

现在,如果你想链接,你可以这样做:

class Chainable 
{ 
    private readonly TextWriter _textWriter; 

    public Chainable(TextWriter textWriter) 
    { 
     _textWriter = textWriter; 
    } 

    public Chainable Write(BaseClass obj) 
    { 
     obj.SerializeTo(_textWriter); 
     return this; 
    } 
} 

然后,你WriteToTextWriter可能是,例如:

public override void WriteToTextWriter(TextWriter textWriter) 
{ 
    new Chainable(textWriter) 
     .Write(_chunk1) 
     .Write(_chunk2) 
     .Write(_chunk3); 
} 

我不确定这是否值得:代码当然更清晰,但由于复杂性增加,对某人(包括您未来的自己)来说,破译会更困难。

编辑:使受保护的抽象方法似乎在这里增加很少,但在生产代码中,额外的层可能会有所帮助。您还需要添加一些开关,以处理格式等。

+0

感谢您的建议。我想我自己可以做到这一点。我主要是想要以某种方式隐藏'public'的'WriteToTextWriter'方法。 – ErikE

+0

@ErikE当然可以。我可能应该有。我假设序列化到一个文件需要传入一个StreamWriter是公开的。但是,当然,应该有一个SerializeTo(StreamWriter)方法或类似方法,它又调用WriteToTextWriter。我会相应地编辑。 – phoog

0

如果你想只有一个类型的instatiated对象,您可以使用Singleton模式(谷歌会给你很多exampels)

如果你有一个全局对象(像一个“为MyApplication”级),你可以在那里提供。

其他方式是一个静态类,它提供了“来自任何地方”的这个单例实例。

您的代码会直接附加到这一个实例...

希望这有助于

+0

这根本没有帮助。我不想要每个Chunk类独立访问的全局单例。请仔细阅读这个问题。范围必须是**每个序列化**而不是**每个全局应用程序/执行**。使用您所建议的模式将会有重复使用以前未清理的StringBuilder的可能性。 – ErikE

+0

对不起,你是对的... – Shnugo

1

看起来像适配器模式的理想候选人。 (未经测试的代码)

public interface IAppendOnly 
{ 
    void Append(string content); 
} 

public class AppendOnlyStringBuilder : IAppendOnly 
{ 
    private StringBuilder _stringBuilder = new StringBuilder() 

    public void Append(string content) 
    { 
    _stringBuilder.Append(content); 
    } 

    public override string ToString() 
    { 
    return _stringBuilder.ToString(); 
    } 
} 

public class Chunk 
{ 
    public void AppendTo(IAppendOnly appendOnly) 
    { 
    appendOnly.Append("My Content"); 
    } 
} 

然后每块工作没有的接口如何实例知识:

_chunk1.AppendTo(aosb); 
_chunk2.AppendTo(aosb); 
_chunk3.AppendTo(aosb); 

但一些事困扰我 - 我倒是喜欢用流畅的界面,以AppendOnlyStringBuilder对象开始,就像我之前的例子。

因此,对于这个需求(减去不必要的AppendOnlyStringBuilder类),然后切换界面方向。

public interface IGetString 
{ 
    string GetString(); 
} 

public Chunk : IGetString 
{ 
    public string GetString() 
    { 
    return "MyContent"; 
    } 
} 

public static class StringBuilderExtensions 
{ 
    public static StringBuilder AppendFrom(this StringBuilder instance 
    , IGetString getString) 
    { 
    instance.Append(getString.GetString()) 
    return instance; 
    } 
} 

然后,它的流利:

var sb = new StringBuilder; 
var a = new Chunk(); 
var b = new Chunk(); 

sb.AppendFrom(a).AppendFrom(b); 
+0

谢谢您的建议!我会仔细考虑这些选项。 – ErikE

+0

这只取决于哪个方向更有意义。由于这些看起来都是抽象类,所以我可以真正知道哪个是Principal和Dependent对象(尽管我的直觉告诉我底部更接近)。 –