Reputation: 31
For example, I have a class with interface, which has few methods. What is the best way to call methods always only in a specific order in the class?
public class SomeClass
{
void Start(ISomeInterface testClass)
{
testClass.Method1();
testClass.Method2();
testClass.Method3();
}
}
public interface ISomeInterface
{
void Method1();//should run 2nd
void Method2();// 1st
void Method3();// 3rd
}
Upvotes: 1
Views: 1863
Reputation: 75619
Only expose the callable methods in an interface, and return a new interface when the method has been called.
interface IMethod1 {
IMethod2 Method1();
}
interface IMethod2 {
IMethod3 Method2();
}
Initially, you return a IMethod1
. This only exposes Method1()
, so it's not possible to call Method2
out of order. When calling Method1()
, it returns an IMethod2
that exposes Method2()
, so that can be called.
These interfaces can be implemented by the same class, which exposes only some methods at a time through the various interfaces.
Edit: I wrote a blog post about this: Enforcing object lifecycles through interfaces
Upvotes: 1
Reputation: 3235
As far as I see, Template method
is not what you are looking for. (Unless you are one of those unpleasant people using answers without accepting ones ;))
If you'd like to give an illusion of freedom to a user and to punish one for using it wrong way, it could be done the following way.
Define an attribute:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class OrderAttribute : Attribute
{
public int Order { get; }
public OrderAttribute(int order) => Order = order;
}
Then define an interface:
public interface IObeyOrder
{
[Order(2)]
[Order(4)]
void Method1(); // should run 2nd or 4th
[Order(1)]
void Method2(); // 1st
[Order(3)]
void Method3(); // 3rd
void Method4(); // order doesn't matter
}
And implement it on a class, calling CheckOrder()
first in each method:
public partial class ObeyOrder : IObeyOrder
{
public void Method1()
{
CheckOrder();
Console.WriteLine("Method1");
}
public void Method2()
{
CheckOrder();
Console.WriteLine("Method2");
}
public void Method3()
{
CheckOrder();
Console.WriteLine("Method3");
}
public void Method4()
{
CheckOrder();
Console.WriteLine("Method4");
}
public void Method5() // non-interface
{
CheckOrder();
Console.WriteLine("Method5");
}
}
where CheckOrder()
is:
public partial class ObeyOrder : IObeyOrder
{
private static readonly Dictionary<string, int[]> orderedMethods = OrderHelper<IObeyOrder>.OrderedMethods;
private readonly Queue<int> orders = new Queue<int>(orderedMethods.Values.SelectMany(i => i).OrderBy(i => i));
private void CheckOrder([CallerMemberName] string methodName = "")
{
if (!orderedMethods.TryGetValue(methodName, out var methodOrders))
return;
var order = orders.Peek();
if (!methodOrders.Contains(order))
throw new Exception($"Wrong method call order. Method '{methodName}' with orders [{string.Join(", ", methodOrders)}]. Expected order {order}.");
orders.Enqueue(orders.Dequeue());
}
}
Of course, you can do it in a non-partial class.
public static class OrderHelper<T>
{
public static Dictionary<string, int[]> OrderedMethods { get; } = typeof(T)
.GetMethods()
.Select(method => new
{
Method = method.Name,
Orders = method.GetCustomAttributes(typeof(OrderAttribute), false)
.Cast<OrderAttribute>()
.Select(attribute => attribute.Order)
.ToArray()
})
.Where(method => method.Orders.Length > 0)
.ToDictionary(method => method.Method, method => method.Orders);
}
Usage:
var obeyOrder = new ObeyOrder();
obeyOrder.Method2(); // should go 1st
obeyOrder.Method4(); // can go whenever, since there is no order attribute
obeyOrder.Method1(); // should go 2nd or 4th
obeyOrder.Method5(); // can go whenever, since it's non-interface
obeyOrder.Method3(); // should go 3rd
obeyOrder.Method1(); // should go 2nd or 4th
obeyOrder.Method2(); // should go 1st (after the last had been already called)
works fine, but
var obeyOrder = new ObeyOrder();
obeyOrder.Method2(); // should go 1st
obeyOrder.Method4(); // can go whenever, since there is no order attribute
obeyOrder.Method1(); // should go 2nd or 4th
obeyOrder.Method5(); // can go whenever, since it's non-interface
obeyOrder.Method3(); // should go 3rd
obeyOrder.Method1(); // should go 2nd or 4th
obeyOrder.Method2(); // should go 1st (after the last had been already called)
obeyOrder.Method2(); // should throw since the 2nd (obeyOrder.Method1()) is expected
throws
Wrong method call order. Method 'Method2' with orders [1]. Expected order 2.
Upvotes: 2
Reputation: 5017
Take a look at Template Method Design Pattern
The intent of Template Method Design Pattern is to define the skeleton of an algorithm in an operation, deferring some steps to client subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
abstract class SomeClass : ISomeInterface
{
public abstract void Method1();
public abstract void Method2();
public abstract void Method3();
// The template method
public void Start()
{
testClass.Method1();
testClass.Method2();
testClass.Method3();
}
}
class ImplementationClass : SomeClass
{
public override void Method1()
{
...
}
public override void Method2()
{
...
}
public override void Method3()
{
...
}
}
// Usage
var implementationClass = new ImplementationClass();
implementationClass.Start();
Upvotes: 4
Reputation: 29222
It's normal to write code so that methods are expected to run in certain order. But in that case we wouldn't want to just expose all of the methods and expect the caller to just "know" to run them in a certain order. If something is required then we must somehow enforce it.
If the methods of an interface can be executed in any order but in one specific case we want to run them in a particular order, that's easy. We just do them in the order we want:
testClass.Method2();
testClass.Method1();
testClass.Method3();
If methods must always be executed in a particular order then it doesn't make sense to expose an interface that allows us to execute them in just any order. The interface should describe how we want the class to be used. In that case this would make more sense:
public interface IDoesSomething
{
void DoSomething();
}
public class DoesSomething : IDoesSomething
{
public void DoSomething()
{
DoAnotherThing();
DoOneThing();
SomethingElse();
}
private void DoOneThing(){}
private void DoAnotherThing(){}
private void SomethingElse(){}
}
Now the interface tells other classes how to interact with it, but the details of how that gets done, which includes a particular sequence of steps, is encapsulated (hidden) inside the implementation of that class.
We're still doing the same thing - breaking a process into steps - but choosing how much of it we expose outside the class. We're making it easier to use our class correctly by making it impossible to use it incorrectly.
Upvotes: 3
Reputation: 1197
First of all i think you got some concepts mixed up, a class implements an interface, you cannot have an interface class. What you do by implementing an interface is ensure that the consumer class of the interface has to implement that method signature in his code.
Secondly, there is no way to execute methods in a certain order if they re in an interface, this is because interface methods (not the code itself from each method, an interface does NOT HAVE ANY LOGIC on it). Probably what you are looking for here is class (can be abstract not sure why do you need an interface though), and you could have this 3 methods as private members of it and have a public method that executes the 3 of them. Like this:
public class Example
{
private void MethodA()
{
//logic from methodA
}
private void MethodB()
{
//logic from methodB
}
private void MethodC()
{
//logic from methodC
}
public void MethodA()
{
MethodB();
MethodA();
MethodC();
}
}
Upvotes: 1