Зависит от того, является ли активация отношения частью поведения системы, которую вы хотите смоделировать.
"Также могут быть дополнительные атрибуты, связанные с отношением."
Так что звучит так, будто вы хотите смоделировать больше, чем просто ссылку.
Содержащий экземпляр b будет работать в простых ситуациях, когда взаимосвязь не является частью поведения системы, а является лишь технической реализацией.
Если вы хотите активировать / деактивировать / активировать (без знания / запоминания активированного объекта B), «нулевое моделирование» не будет работать (потому что вы потеряли бы ссылку и потеряли бы информацию о взаимосвязи). Или, например, когда на более позднем этапе вы захотите повторно активировать отношения без ссылки на a и b, но на отношения.
Таким образом, видя отношения как первоклассный гражданин модели.
Затем вы можете сделать что-то вроде этого (но это может быть излишним, и просто показать моделирование отношений как часть вашего домена):

(источник: googlecode.com )
ObjectA использует прокси ObjectB, который будет действовать как ObjectB, но на самом деле он имеет ссылку на однонаправленное отношение к ObjectB.
Прокси-объект ObjectB может реализовывать поведение в отношении того, что делать, когда связь не активна.
В коде:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ActivatableRelationshipExample
{
public interface IActivatable
{
void Activate();
void Deactivate();
bool IsActive { get; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ActivatableRelationshipExample
{
public interface IRelationShip<TSource,TDestination> : IActivatable
{
TDestination Destination { get; }
TSource Source { get; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ActivatableRelationshipExample
{
public class ObjectA
{
// you could also have an ObjectB reference here
// and fill it with an ObjectBProxy instance
// in the constructor. thought this would
// explain the example a bit better
private readonly ObjectBProxy proxy;
public ObjectA(ObjectB b)
{
proxy = new ObjectBProxy(b);
}
public void AMethodUsingB()
{
proxy.BMethod();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ActivatableRelationshipExample
{
public class ObjectB
{
public virtual void BMethod()
{
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ActivatableRelationshipExample
{
public class ObjectBProxy : ObjectB, IActivatable
{
private readonly UniDirectionalRelationship<ObjectB> relation;
public ObjectBProxy(ObjectB to)
{
this.relation = new UniDirectionalRelationship<ObjectB>(to);
}
public override void BMethod()
{
if (relation.IsActive)
{
relation.Destination.BMethod();
} else
{
// do some specific logic here
}
}
public void Activate()
{
relation.Activate();
}
public void Deactivate()
{
relation.Deactivate();
}
public bool IsActive
{
get { return relation.IsActive; }
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ActivatableRelationshipExample
{
public class RelationshipNotActiveException : Exception
{
public RelationshipNotActiveException(string message) : base(message)
{
}
public RelationshipNotActiveException(string message, Exception innerException) : base(message, innerException)
{
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ActivatableRelationshipExample
{
public class UniDirectionalRelationship<TDestination> : IRelationShip<object,TDestination> where TDestination :class
{
private bool active = true;
private readonly TDestination destination;
public UniDirectionalRelationship(TDestination destination)
{
if(destination == null)
{
throw new ArgumentNullException("destination","destination cannot be null");
}
this.destination = destination;
}
public void Activate()
{
active = true;
}
public void Deactivate()
{
active = false;
}
public virtual bool IsActive
{
get { return active; }
}
public virtual TDestination Destination
{
get
{
if (active)
{
return destination;
}
throw new RelationshipNotActiveException("the relationship is not active");
}
}
public virtual object Source
{
get { throw new NotSupportedException("Source is not supported"); }
}
}
}