简体   繁体   中英

Initializing property value in one class using the return value of a method in a different class

I have the following structure:

public class LogicStatement : ILogicStatement
{
    public string TestLogic { get; set; }
    public string CompareLogic { get; set; }
    public string Operator { get; set; }
    public string Expression();
    public bool Value();
}

public class Test : ITest 
{
    public int TestId { get; set; }
    public int LiteralId { get; set; }
    public string TestName { get; set; }
    public string TestText { get; set; }
    public string TestDisplayName { get; }
    **public ILogicStatement LogicStatement { get; set; }**
    public string Expression { get; set; }
    public bool Value { get; set; }
}

public class Literal : ILiteral
{
    some property members...
    **public List<ITest> Tests {get; set;}**
    some method members...
}

Note that the class Test has a member of type LogicStatement, and the class Literal has a member of type List.

Note also that all classes have properties and methods that share the same name: Expression, Value, Expression(), Value().

The value of Expression and Value (properties and methods) depend on values in the LogicStatement class.

Throughout the whole project, I use the Interface Type for to instantiate each object to adhere with Dependency Inversion. To support this, I use a factory-like design to create new instances of Test and LogicStatement.

Example:

public static class Factory
{
    public static ILogicStatement CreateLogicStatement()
    {
        return new LogicStatement();
    }
    public static ITest CreateTest()
    {
        return new Test(CreateLogicStatement());
    }
    public static List<ITest> CreateTests()
    {
        return new List<ITest>();
    }
//repeat the same for evey other class.
}

My goal is to have Expression() and Value() be calculated only once in the bottom level class (LogicStatement), and somehow get transfered to their counterpart properties in the higher level classes.

I'm getting the data from Dapper and it looks like all the nested objects are returned from the Dapper module correctly with the same nested structure I intended, and with the right values for all of their members. All of them but Expression, Expression(), Value, Value() are null.

my constructors look like this:

public LogicStatement()
{
    Expression();
    Value();
}

public Test(ILogicStatement logicStatement)
{
    _logicStatement = logicStatement;
    Expression = _logicStatement.Expression();
    Value = _logicStatement.Value();
}

public Literal(ITest test)
{
    _test = test;
    Expression = _test.Expression;
    Value = _test.Value;
}

and my main:

List<ILiteral> literals = Factory.CreateLiterals();
List<ITest> tests = Facotry.CreateTests();
List<ILogicStatement> logicStatements = Factory.CreateLogicStatements();

literals = GetDataFromDapper();

This last line seems to assign correct values to all other members on all hierarchies. But I cannot get Expression and Value to be anything other than null.

If I test LogicStatement.Expression() and LogicStatement.Value() standalone, they do return the expexted values. but starting at the first parent class Test, these properties are all null.

I think I'm doing something wrong in the way i'm instantiating my objects. Primarily because I'm not sure i understand basic best practices to write constructors.

Maybe I the desired behavior should be implemented through events, where the Test and Literal classes subscribe to changes in the Expression() and Value() methods (or rather to what calculates them). But I never used events and I'd like to know if this fundamentally can be acheived without them first.

My question: How do I make the Expression() Value() at the bottom level class "Fire up" whenever LogicStatement is instantiated, and then have the Expression and Value properties be assigned accordingly as a result.

In other words, I want the following to always be true:

test[i].Expression == literal[i].Expression == LogicStatement[i].Expression()

I'm a beginner in OOP. So any fundamental explanation is welcome.

As you are new to object oriented programming I would start with the basics and leave factories and adhering with Dependency Inversion and the interfaces away for later.

You could tell Dapper to split joined tables into multiple entities (see https://www.learndapper.com/relationships ), but for learning OOP I would start doing everything manually.

Your class design does not look proper to me yet. Not sure what Expression and Value of the LogicStatement are, but if they are calculations based on the other properties, I would implement them as (just to show off with complicated words) lazy initialized cached getter properties that are invalidated in the setters of the relevant properties. That ensures you only calculate them once for as many reads you like but recalculate them on first read after one or multiple properties have been updated.

public class LogicStatement {

    private string _testLogic;
    private string _compareLogic;
    private string _operator;
    private string? _expression;
    private bool? _value;

    public LogicStatement(string testLogic, string compareLogic, string @operator) {
        _testLogic = testLogic;
        _compareLogic = compareLogic;
        _operator = @operator;
    }

    public string TestLogic {
        get {
            return _testLogic;
        }
        set {
            _testLogic = value;
            InvalidateCachedValues();
        }
    }

    public string CompareLogic {
        get {
            return _compareLogic;
        }
        set {
            _compareLogic = value;
            InvalidateCachedValues();
        }
    }

    public string Operator {
        get {
            return _operator;
        }
        set {
            _operator = value;
            InvalidateCachedValues();
        }
    }

    public string Expression {
        get {
            string? result = _expression;
            if (result is null) {
                _expression = result = BuildExpression();
            }
            return result;
        }
    }

    public bool Value {
        get {
            bool? result = _value;
            if (result is null) {
                _value = result = EvaluateValue();
            }
            return result.Value;
        }
    }

    private void InvalidateCachedValues() {
        _expression = null;
        _value = null;
    }

    private string BuildExpression() {
        //Your logic goes here
        throw new NotImplementedException();
    }

    private bool EvaluateValue() {
        //Your logic goes here
        throw new NotImplementedException();
    }

}

Sorry, it got a bit bigger with the full properties.

In the other classes I would not copy the Value and the Expression but simply remove these properties as anybody can easily access them through the LogicStatement property:

public class Test {

    public Test(int testId, int literalId, string testName, string testText, string testDisplayName, LogicStatement logicStatement) {
        TestId = testId;
        LiteralId = literalId;
        TestText = testText;
        TestDisplayName = testDisplayName;
        LogicStatement = logicStatement;
    }

    public int TestId { get; }
    public int LiteralId { get; }
    public string TestName { get; }
    public string TestText { get; }
    public string TestDisplayName { get; }
    public LogicStatement LogicStatement { get; }

}

and the Literal could look like this (I got a bit confused whether this class has one Test or a list of them, I stick to your constructor + properties that hint in the direction of a single one):

public class Literal {

    private Test _test;

    public Literal(string property1, int property2, Test test) {
        Property1 = property1;
        Property2 = property2;
        _test = test;
    }

    public string Property1 { get; }
    public int Property2 { get; }
    public string Expression => _test.LogicStatement.Expression;
    public bool Value => _test.LogicStatement.Value;

}

As you decided not to expose the Test in the Literal it makes sense to provide Expression and Value , otherwise they could also be removed (or kept for convenience).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM