Representing complex object dependencies

I have several classes with a reasonably complex (but acyclic) dependency graph. All the dependencies are of the form: class X instance contains an attribute of class Y. All such attributes are set during initialization and never changed again.

Each class’ constructor has just a couple parameters, and each object knows the proper parameters to pass to the constructors of the objects it contains. class Outer is at the top of the dependency hierarchy, i.e., no class depends on it.

Currently, the UI layer only creates an Outer instance; the parameters for Outer constructor are derived from the user input. Of course, Outer in the process of initialization, creates the objects it needs, which in turn create the objects they need, and so on.

The new development is that the a user who knows the dependency graph may want to reach deep into it, and set the values of some of the arguments passed to constructors of the inner classes (essentially overriding the values used currently). How should I change the design to support this?

I could keep the current approach where all the inner classes are created by the classes that need them. In this case, the information about “user overrides” would need to be passed to Outer class’ constructor in some complex user_overrides structure. Perhaps user_overrides could be the full logical representation of the dependency graph, with the overrides attached to the appropriate edges. Outer class would pass user_overrides to every object it creates, and they would do the same. Each object, before initializing lower level objects, will find its location in that graph and check if the user requested an override to any of the constructor arguments.

Alternatively, I could rewrite all the objects’ constructors to take as parameters the full objects they require. Thus, the creation of all the inner objects would be moved outside the whole hierarchy, into a new controller layer that lies between Outer and UI layer. The controller layer would essentially traverse the dependency graph from the bottom, creating all the objects as it goes. The controller layer would have to ask the higher-level objects for parameter values for the lower-level objects whenever the relevant parameter isn’t provided by the user.

Neither approach looks terribly simple. Is there any other approach? Has this problem come up enough in the past to have a pattern that I can read about? I’m using Python, but I don’t think it matters much at the design level.

4

You should look at all of the common object creation patterns and make an assessment based upon the state of your logic, time you have to implement the change, and so forth. Also, rather than have the participating objects know how to construct each other, you should consider Inversion of Control.

Its hard to be specific without more info, but given that you are talking about a complex graph I would think that Abstract Factory or Builder would be more useful here than just a simple Factory depending on how different each of your scenarios is.

If you have some initial decisions to make that influence creation of the graph but the differences are nuances, look at Builder…particularly if there are some structural differences between the “products” (such as, some participating objects are optional or in some scenarios participating objects drag in another set of collaborators).

If the differences between the scenarios are more distinct / elaborate and take the form of “families” of objects that should be used together, look at Abstract Factory with a few different factory implementations.

If you actually just have a few specific flavors of this graph and want to pick and choose between them dynamically, you might be able to just go with Prototype; treat your current implementation as a prototype, code up your alternate implementation(s) as another prototype, and just clone the appropriate prototype as needed.

4

I would move all object construction logic out of Outer into a different class using the Factory_pattern.

The Constructors of domain-classes ´Outer´ and ´Inner´ get all required child parameters

public Outer(Inner inner)
{
    _inner = inner;
}

and the factory create the items for you:

public class OuterFactory
{
    // Existing behaviour - concrete Inner instantiation is hard coded
    public virtual Outer createOuter(CreationParameters parameters)
    {
        Inner _inner = createInner(parameters);
        Outer outer = new Outer(inner);

        return outer;           
    }

    public virtual Inner createInner(CreationParameters parameters)
    {
        Inner _inner = new Inner();     
        return _inner;          
    }
}

If you need a different inner class you can easily create a new factory that derives from the base factory:

public class MySpecialOuterFactory : OuterFactory
{
    public overrrde Inner createInner(CreationParameters parameters)
    {
        Inner _inner = new SpecialInner();      
        return _inner;          
    }
}

2

An approach using reflection might work well here.

So, assuming if it’s ok to set up the graph in the default way, and then set values that you wish to override after (which requires mutability of your objects, obviously):

Have your object graph set up in the default way. Then use reflection to walk the object graph. For each object you find in the graph, check for any properties represented in your user_overrides helper structure for overriding. If you find a match, set the property to the new value (again, using reflection).

Depending on your exact scenario, you may just be able to match against class types in your user_overrides structure; alternatively, you might have to store keypaths, by which I mean something representing the route to a certain class in the object graph.

You may have to be careful about the order in which you override things in the object tree.

There’s plenty of info out there about reflection.

1

Your description of user_overrides is beginning to sound a bit like the strategy pattern – where objects outside of the hierarchy pass a strategy (callback) for creating the dependency..

example in pseudocode

type OuterType
{
    private inner : InnerType

    // Existing behaviour - concrete Inner instantiation is hard coded
    OuterConstructor()
    {
        _inner = new Inner()
    }

    // Strategy pattern, instantiation strategy is passed from outside as
    // a callback function which returns an Inner instance
    OuterConstructor(func() innerBuilderCallback)
    {
        _inner = innerBuilderCallback()
    }
}

What happens if Inner also has a dependency which you want to expose with an instantiation strategy? You don’t have to pass both strategies to the Outer constructor, since that strategy will itself ‘chain’ the nested strategy

void Foo()
{
    func innerBuilderCallback = lambda: new Inner(lambda: new InnerDependency());

    var outer = new Outer(innerBuilderCallback);
}

Depending on how deep your hierarchy goes, you might find that you end up with quite a few of these callback strategies which becomes a little difficult to manage. In that case, encapsulate the whole process in a Factory object.

1

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật