记录同一接口的两个不同类

时间:2017-10-27 17:04:38

标签: c#

我正在尝试为相同接口类型的两个类创建一个装饰器Logging类,它将占用该类并基本上覆盖其方法步骤,以便它可以打印一些额外的东西。我发现这个解决方案真的很糟糕,因为我重新实现已经实现的东西,但我想不出更好的解决方案。

public class A :IA
{
    private int state = 0;
    public void printStep()
    {
        Console.WriteLine("A state: {0}", state);
    }

    public bool Step()
    {
        state++;
        return true;
    }

    public void Run()
    {
        for (int i = 0; i < 5; ++i)
        {
            Step();
        }
    }
}

public class B : IA
{
    private double state = 0.0;
    public void printStep()
    {
        Console.WriteLine("B state: {0}", state);
    }

    public bool Step()
    {
        state+= 0.1;
        return true;
    }

    public void Run()
    {
        for (int i = 0; i < 4; ++i)
        {
            Step();
        }
    }
}

public interface IA
{
    void printStep();
    bool Step();
    void Run();
}

public class Logger
{
    private IA ia;
    public Logger(IA ia)
    {
        this.ia = ia;
    }

    public void Run() //Don't like this method especially because it shouldn't reimplement the run methods again
    {
        if (ia.GetType() == typeof(A))
        {
            for (int i = 0; i < 5; ++i)
            {
                ia.printStep();
                ia.Step();
            }
        }
        else
        {
            for (int i = 0; i < 4; ++i)
            {
                ia.printStep();
                ia.Step();
            }
        }
    }
}

任何想法如何做到这一点?

编辑:我想做的是这样的事情

 public override bool Step()
 {
     var ret = base.Step();
     base.printStep();
     return ret;
 }

编辑2:我需要调用原始的Run方法,该方法将使用添加的日志记录调用更新的Step方法。这个简单例子的预期行为是A类记录器将打印符号A 5次,B类记录器打印符号B 4次。

编辑3:为什么我要尝试做这种行为?我觉得有两个A类和B类可以完全正常工作而不需要自己编写任何信息。记录器应该采用其中任何一个并记录每个步骤后通常会改变的每个状态。所以我会得到一个完美的信息。我可以玩A类和B类中添加一个布尔变量记录,但感觉不对。

public class A :IA
{
...
    public bool Step()
    {
        if (logging) printStep();
        state++;
        return true;
    }
    ...

编辑5:更改了初始代码,使其更有意义。 使用:

 var a = new A();
 a.Run();
 var b = new B();
 b.Run();
 var loggerA = new Logger(a);
 loggerA.Run();
 var loggerB = new Logger(b);
 loggerB.Run();

预期产出:

A state: 5
A state: 6
A state: 7
A state: 8
A state: 9
B state: 0.4
B state: 0.5
B state: 0.6
B state: 0.7

3 个答案:

答案 0 :(得分:0)

如果您希望Logger类实现Decorator模式,它必须实现与“装饰”类相同的接口(而不仅仅是将它们封装在您的代码片段中)。

通过这样做,您将能够实现所需的行为(类似于您的编辑中的行为)。

编辑: 假设这是记录器记录某些内容的责任,IA不必包含printStep()方法。

因此我们有:

public interface IA
{
    bool Step();
}


public class A : IA
{
    public bool Step()
    {
        return true;
    }
}

public class B : IA
{
    public bool Step()
    {
        return true;
    }
}


public class Logger : IA
{
    private readonly IA _decorated;

    public Logger(IA decorated)
    {
        _decorated = decorated;
    }

    public bool Step()
    {
        Console.WriteLine("Before Step...");
        _decorated.Step();
        Console.WriteLine("Step completed.");
    }
}

实例化Logger时,您需要传递要装饰的对象

IA notDecorated = new A();
IA decorated = new Logger(notDecorated);

答案 1 :(得分:0)

如果没有其他对象直接调用Step()的原因,则它不应该成为界面的一部分。但是,由于我认为您的课程形状大致相似但实施方式可能非常不同,我不认为装饰这些课程会让您得到您想要的东西。我认为只需添加日志记录就更有意义了。

我提供了一个现代日志框架(如NLog)为您做的玩具示例。在我的示例中,您可以通过传入不同的日志记录实现来更改是否有日志记录。在实际框架中,您可以配置不同级别的日志记录,以便可以完全关闭日志记录或仅记录更重要的信息。

public class A : IA
{
    private readonly ILog logger;

    public B(ILog logger)
    {
        this.logger = logger;
    }

    private bool Step()
    {
        // some logic
        logger.Info("Something specific here.")
        // more logic
        return true;
    }

    public void Run()
    {
        for (int i = 0; i < 5; ++i)
        {
            logger.Info("A");
            Step();
        }
    }
}

public class B : IA
{
    private readonly ILog logger;

    public B(ILog logger)
    {
        this.logger = logger;
    }

    private bool Step()
    {
        return true;
    }

    public void Run()
    {
        for (int i = 0; i < 4; ++i)
        {
            logger.Info("B");
            Step();
        }
    }
}

public interface IA
{
    void Run();
}

public interface ILog
{
    void Info(string message);
}

public class ConsoleLogger : ILog
{
    public void Info(string message)
    {
        Console.WriteLine(message);
    }
}

public class NoopLogger : ILog
{
    public void Info(string message)
    {

    }
}

然后,当您实例化类时,可以传入您需要的任何记录器。

var noopLogger = new NoopLogger();
var a = new A(noopLogger);
a.Run();
var b = new B(noopLogger);
b.Run();

var consoleLogger = new ConsoleLogger();
a = new A(consoleLogger);
a.Run();
b = new B(consoleLogger);
b.Run();

答案 2 :(得分:0)

如果你想摆脱A和B中的共享功能,你可以创建一个抽象类。在此处实现共享功能,并将其余方法抽象化。如果您严格要使用装饰器模式,则可以为抽象类创建一个接口。

public class A : AbstractA 
{ 
    public override void printStep()
    {
        Console.WriteLine("A"); 
    }

    public override void Run() 
    { 
        base.Run();
        for (int i = 0; i < 5; ++i) 
        { 
            Step(); 
        } 
    } 
} 

public class B : AbstractA 
{ 
    public override void printStep()
    {
        Console.WriteLine("B"); 
    }

    public override void Run() 
    { 
        base.Run();
        for (int i = 0; i < 4; ++i) 
        { 
            Step(); 
        }
    } 
} 

public abstract class AbstractA : IA
{ 
    public abstract void printStep(); 

    public virtual bool Step() 
    {
        return true;
    }

    public virtual void Run()
    {
        printStep();
    }
} 


public interface IA 
{ 
    void printStep(); 
    bool Step(); 
    void Run(); 
}

public class Logger 
{ 
    private IA ia; 

    public Logger(IA ia) 
    { 
        this.ia = ia; 
    }

    void Run()
    {
        ia.Run();
    }
}