It looks like you want transformations:
public abstract class TransformationStep<TIn, TOutput>
{
public abstract TOutput Execute(TIn input);
}
And if you want to just return input type, then it is possible to create another method with return type:
public abstract class TransformationStep<TIn, TOutput>
{
public abstract TOutput Execute(TIn input);
public abstract TIn ExecuteWithoutTransformation(TIn input);
}
Dataflow
If you want to connect chains of data into pipeline or graph, then you can use TransformBlock.
What is about Chain of Responsibility pattern?
As wiki says about "Chain of Responsibility pattern":
In object-oriented design, the chain-of-responsibility pattern is a
behavioral design pattern consisting of a source of command objects
and a series of processing objects.2 Each processing object contains
logic that defines the types of command objects that it can handle;
the rest are passed to the next processing object in the chain. A
mechanism also exists for adding new processing objects to the end of
this chain.
Your code looks similar, however, code and goal of chain responsibility pattern is slightly different. It does not make transformations, it gives object to the next processing object in the chain.
So one of the variations of code of chain of the responsibility pattern can look like this:
An abstraction of desired behaviour of chain of the responsibility pattern:
public abstract class MyHandler<T>
{
private MyHandler<T> Next { get; set; }
public virtual void Handle(T request)
{
Next?.Handle(request);
}
public MyHandler<T> SetNext(MyHandler<T> next)
{
Next = next;
return Next;
}
}
And let us imagine that we are publishing house and we want that each property of article should be validated.
So, concrete implemetations of handling article can look like this:
public class OnlyNewArticleValidationHandler : MyHandler<Document>
{
public override void Handle(Document document)
{
if (document.DateCreated.Year < DateTime.Now.Year)
{
throw new Exception("Only new articles should be published.");
}
base.Handle(document);
}
}
public class AuthorValidationHandler : MyHandler<Document>
{
public override void Handle(Document document)
{
if (string.IsNullOrWhiteSpace(document.Author))
{
throw new Exception("Author is required.");
}
base.Handle(document);
}
}
public class NameRequiredValidationHandler : MyHandler<Document>
{
public override void Handle(Document document)
{
if (string.IsNullOrWhiteSpace(document.Name))
{
throw new Exception("Name is required.");
}
base.Handle(document);
}
}
And ArticleProcessor
would look like this:
public class MyChainAticleProcessor
{
public void Validate(Document document)
{
var handler = new NameRequiredValidationHandler();
handler.SetNext(new AuthorValidationHandler())
.SetNext(new OnlyNewArticleValidationHandler());
handler.Handle(document);
}
}
And it can be run like this:
new MyChainAticleProcessor().Validate(
new Document { Author = "Author 1", Name="Name 1" }
);