1

Considering the rules of good OOP and design, is it correct to write a method in an interface which has as input types or output types classes which implements the interface?

I give you an example of a very simple exercise I must do, regarding complex numbers:

public interface IComplex
{
    double GetImaginary();

    double GetReal();
    
    ComplexImpl Conjugate();

    ComplexImpl Add(ComplexImpl y);

}

/*----------------------------------------------------*/


public class ComplexImpl : IComplex
    {
    private double real;

    private double imaginary;

    public ComplexImpl(double real, double imaginary) { this.real = real; this.imaginary = imaginary; }

    public double GetReal() { return this.real; }

    public double GetImaginary() { return this.imaginary; }

    public ComplexImpl Conjugate()
    {
        ComplexImpl conjugate = new ComplexImpl(this.real, -this.imaginary);
        return conjugate;
    }

    public ComplexImpl Add(ComplexImpl y)
    {
        ComplexImpl result = new ComplexImpl(this.real + y.real, this.imaginary + y.imaginary);
        return result;

    }

}

Considering the interface IComplex: is it correct to write the methods Conjugate and Add such that they have as input (and/or output) objects which are instantiations of the class ComplexImpl?

Consider that the class ComplexImpl implements the interface when this methods are defined.

EDIT:

For those of you who answer: first of all thank you, however I have the following problem. If I substitute "ComplexImpl" with "IComplex" both in the interface and in the class, I obtain this error in the method Add:

"'IComplex' does not contain a definition for 'imaginary' and no accessible extension method 'imaginary' accepting a first argument of type 'IComplex' could be found".

The only way to solve this is through the use of Generics?

  • I'd argue this is neither "whrong" nor "correct", but just not good design, to make an interface depending on its implementation. However from a syntacical point of view this is absolutely fine. – MakePeaceGreatAgain Aug 17 '22 at 14:09

4 Answers4

3

I suggest you use the interface IComplex instead of ComplexImpl in the interface. This will still yield the desired results without relying on an implementation of the interface.

public interface IComplex
{
    double GetImaginary();

    double GetReal();
    
    IComplex Conjugate();

    IComplex Add(IComplex y);

}

This will be your concrete type implementation:


        public class ComplexImpl : IComplex
        {
            private double real;

            private double imaginary;

            public ComplexImpl(double real, double imaginary) { this.real = real; this.imaginary = imaginary; }

            public double GetReal() { return this.real; }

            public double GetImaginary() { return this.imaginary; }

            public IComplex Conjugate()
            {
                ComplexImpl conjugate = new ComplexImpl(this.real, -this.imaginary);
                return conjugate;
            }

            public IComplex Add(IComplex y)
            {
                ComplexImpl result = new ComplexImpl(this.real + y.GetReal(), this.imaginary + y.GetImaginary());
                return result;

            }

        }

Note that you cannot access private members when referring to an interface. All interface definitions are public. Therefore you cannot use y.imaginary for example but since the interface defines an accessor for this private field GetImaginary(), you can use that.

Bron Davies
  • 5,930
  • 3
  • 30
  • 41
  • Thank you for your answer. I answered you on the post, under the word "Edit" – claudioclaudio Aug 17 '22 at 14:27
  • @claudioclaudio I updated the answer to address the other issue. – Bron Davies Aug 17 '22 at 19:34
  • Thank you. When I use the methods "Conjugate" and "Add" to create an instance of the class "ComplexImpl" the return type is IComplex. Must I use the casting to "ComplexImpl"? Besides the compiling, can this casting create problems? – claudioclaudio Aug 18 '22 at 08:33
  • No, it's OK to cast it. But in most situations, developers usually just refer to the interface so that the implementation can be swapped out. – Bron Davies Aug 18 '22 at 12:52
1

This thing is breaking the reason to use interfaces: To abstract implemntations. An easy and bearable fix to this would be:

public interface IComplex
{
   double GetImaginary();

   double GetReal();

   IComplex Conjugate();

   IComplex Add(IComplex y);

}

This way an implmentation is returned, but the interface stays clean and has no knowledge about an implementation.

Rafalon
  • 4,450
  • 2
  • 16
  • 30
Patrick
  • 387
  • 3
  • 15
0

You can make the interface or methods generic, constrained over your interface.

Choice 1

public interface IComplex<TComplex>
    where TComplex : IComplex
{
    double GetImaginary();

    double GetReal();
    
    TComplex Conjugate();

    TComplex Add(TComplex y);
}

Choice 2

public interface IComplex
{
    double GetImaginary();

    double GetReal();
    
    TComplex Conjugate<TComplex>() where TComplex : IComplex;

    TComplext Add<TComplex>(TComplex y) where TComplex : IComplex;
}
Tanveer Badar
  • 5,438
  • 2
  • 27
  • 32
  • Thank you, can I solve this problem with the method "Add" only using Generics? – claudioclaudio Aug 17 '22 at 14:28
  • You can. And you can also ditch the generics and directly use `IComplex` in your interface like others have already mentioned. How you choose to do it is completely up to you. There are different trade offs involved in all these choices. – Tanveer Badar Aug 17 '22 at 14:29
0

I think a better design would be to use IComplex in the method signatures in the interface:

public interface IComplex
{   
    double GetImaginary();

    double GetReal();


    // Changed:
    
    IComplex Conjugate();

    IComplex Add(IComplex y);
}

and call .GetReal() and .GetImaginary() (rather than getting the real and imaginary values directly) inside both Conjuate() and Add():

public class ComplexImpl : IComplex
{
    private double real;

    private double imaginary;

    public ComplexImpl(double real, double imaginary)
    {
        this.real = real;
        this.imaginary = imaginary;
    }

    public double GetReal() { return this.real; }

    public double GetImaginary() { return this.imaginary; }


    // Changed:

    public IComplex Conjugate()
    {
        ComplexImpl conjugate = new ComplexImpl(this.GetReal(), -this.GetImaginary());

        return conjugate;
    }

    public IComplex Add(IComplex y)
    {
        ComplexImpl result = new ComplexImpl(
            this.GetReal() + y.GetReal(), this.GetImaginary() + y.GetImaginary());

        return result;
    }
}
Astrid E.
  • 2,280
  • 2
  • 6
  • 17
  • Thank you. When I use the methods "Conjugate" and "Add" to create an instance of the class "ComplexImpl" the return type is IComplex. Must I use the casting to "ComplexImpl"? Besides the compiling, can this casting create problems? – claudioclaudio Aug 18 '22 at 08:33
  • Yes, you would need to cast the result to `ComplexImpl` (e.g. `ComplexImpl complexResult = (ComplexImpl)complexA.Add(complexB)`). If you do the casting directly (like in the example just stated), it should not create any problems. If you introduce another class that implements `IComplex` and try to cast between the two implementations, though, you will run into problems. A simple example of such a problematic casting scenario is described by Eddie in [this answer](https://stackoverflow.com/questions/539436/cast-interface-to-its-concrete-implementation-object-or-vice-versa#answer-539523). – Astrid E. Aug 18 '22 at 10:49