Referência circular - questão de arquitetura

moiv

Esta é provavelmente uma pergunta para iniciantes, mas eu pesquisei muitos tópicos e não consegui encontrar a mesma situação, embora tenha certeza de que esse tipo de situação acontece o tempo todo.

Meu projeto / programa rastreará alterações nos desenhos em projetos de construção e enviará notificações às pessoas quando os desenhos forem alterados.

Haverá muitos projetos de construção (canteiros de obras), que por sua vez terão muitos desenhos em cada um. Cada desenho terá algumas revisões (conforme eles são alterados, uma nova revisão é criada).

Aqui está minha aula de projeto

public class Project
{
    private readonly List<Drawing> _drawings = new List<Drawing>(30);
    private readonly List<Person> _autoRecepients = new List<Person>(30);

    public int ID { get; private set; }
    public string ProjectNumber { get; private set; }
    public string Name { get; private set; }
    public bool Archived { get; private set; }
    public List<Person> AutoRecepients { get { return _autoRecepients; } }


    public Project(int id, string projectNumber, string name)
    {
        if (id < 1) { id = -1; }

        ID = id;
        ProjectNumber = projectNumber;
        Name = name;
    }


    public bool AddDrawing(Drawing drawing)
    {
        if (drawing == null) return false;
        if (_drawings.Contains(drawing)) { return true; }

        _drawings.Add(drawing);

        return _drawings.Contains(drawing);
    }


    public void Archive()
    {
        Archived = true;
    }

    public bool DeleteDrawing(Drawing drawing)
    {
        return _drawings.Remove(drawing);
    }

    public IEnumerable<Drawing> ListDrawings()
    {
        return _drawings.AsReadOnly();
    }

    public override string ToString()
    {
        return string.Format("{0} {1}", ProjectNumber, Name);
    }
}

Aqui está minha aula de desenho

public class Drawing : IDrawing
{
    private List<IRevision> _revisions = new List<IRevision>(5);
    private List<IssueRecord> _issueRecords = new List<IssueRecord>(30);
    private IRevision _currentRevision;

    public int ID { get; private set; }
    public string Name { get; private set; }
    public string Description { get; set; }
    public Project Project { get; private set; }
    public IRevision CurrentRevision { get { return _currentRevision; } }


    public Drawing(int id, string name, string description, Project project)
    {
        // To be implemented
    }


    /// <summary>
    /// Automatically issue the current revision to all Auto Recepients
    /// </summary>
    public void AutoIssue(DateTime date)
    {
        AutoIssue(date, _currentRevision);
    }

    /// <summary>
    /// Automatically issue a particular revision to all Auto Recepients
    /// </summary>
    public void AutoIssue(DateTime date, IRevision revision)
    {

    }

    public void IssueTo(Person person, DateTime date, IRevision revision)
    {
        _issueRecords.Add(new IssueRecord(date, this, revision, person));

        throw new NotImplementedException();
    }


    public void IssueTo(Person person, DateTime date)
    {
        IssueTo(person, date, _currentRevision);
    }        

    public void IssueTo(IEnumerable<Person> people, DateTime date)
    {
        IssueTo(people, date, _currentRevision);
    }

    public void IssueTo(IEnumerable<Person> people, DateTime date, IRevision revision)
    {
        foreach (var person in people)
        {
            IssueTo(person, date, revision);
        }

    }

    public void Rename(string name)
    {
        if (string.IsNullOrWhiteSpace(name)) { return; }

        Name = name;
    }

    public void Revise(IRevision revision)
    {
        if (revision.Name == null ) return;

        _revisions.Add(revision);
        _currentRevision = revision;
    }

    public struct IssueRecord
    {
        public int ID { get; private set; }
        public DateTime Date { get; private set; }
        public IDrawing Drawing { get; private set; }
        public IRevision Revision { get; private set; }
        public Person Person { get; private set; }

        public IssueRecord(int id, DateTime date, IDrawing drawing, IRevision revision, Person person)
        {
            if (id < 1) { id = -1; }

            ID = id;
            Date = date;
            Drawing = drawing;
            Revision = revision;
            Person = person;
        }

    }
}

E aqui está a estrutura de revisão

public struct Revision : IRevision
{        
    public int ID { get; private set; }
    public string Name { get; }
    public DateTime Date { get; set; }
    public IDrawing Drawing { get; }
    public IDrawingFile DrawingFile { get; private set; }

    public Revision(int id, string name, IDrawing drawing, DateTime date, IDrawingFile drawingFile)
    {
        if (name == null) { throw new ArgumentNullException("name", "Cannot create a revision with a null name"); }
        if (drawing == null) { throw new ArgumentNullException("drawing", "Cannot create a revision with a null drawing"); }
        if (id < 1) { id = -1; }

        ID = id;
        Name = name;
        Drawing = drawing;
        Date = date;
        DrawingFile = drawingFile;
    }

    public Revision(string name, IDrawing drawing, DateTime date, IDrawingFile drawingFile)
        : this(-1, name, drawing, date, drawingFile)
    {

    }

    public Revision(string name, IDrawing drawing)
        : this(-1, name, drawing, DateTime.Today, null)
    {

    }

    public void ChangeID(int id)
    {
        if (id < 1) { id = -1; }

        ID = id;
    }

    public void SetDrawingFile(IDrawingFile drawingFile)
    {
        DrawingFile = drawingFile;
    }
}

Minha pergunta é sobre a referência do projeto na classe de desenho e a referência do desenho na estrutura de revisão. Parece um cheiro de código? Também parece que pode causar problemas com a serialização no futuro. Existe uma maneira melhor de fazer isso?

Parece necessário que um objeto de desenho saiba a que projeto pertence, para que, se estiver trabalhando com objetos de desenho individuais, eu possa saber a que projeto eles pertencem.

Da mesma forma, cada revisão é essencialmente "propriedade" de um desenho ou parte dela. Uma revisão não faz sentido sem um desenho, então ela precisa de uma referência ao desenho ao qual pertence?

Qualquer conselho seria muito apreciado.

Chris F Carroll

O que você tem não são tanto referências circulares, mas dois exemplos de

um relacionamento pai-filho que é navegável de ambas as extremidades.

Sim, é normal e aceitável e não, não é um cheiro de código. Sim, algumas ferramentas de serialização exigem que você dê dicas. por exemplo, Newtonsoft.Json deseja a ReferenceLoopHandling.Ignoreconfiguração.

Navegabilidade como um conceito nem sempre é falado no design OO, o que é lamentável porque é exatamente o conceito que você deseja aqui. (É um termo explícito em UML).

Muitas vezes você não precisa de navegabilidade de ambas as extremidades. Os relacionamentos entre pais e filhos costumam ser codificados apenas de pai para filho. Isso é muito comum. Por exemplo, uma invoicelineclasse raramente precisa de um campo explícito para seu pai invoiceporque a maioria dos aplicativos só olha a linha depois de recuperar a fatura pai.

Portanto, a decisão de design não é,

"Uma revisão faz sentido sem um desenho?"

Mas

"Será que algum dia terei de encontrar um desenho com apenas uma revisão?"

Meu palpite é que suas revisões são como linhas de fatura e não precisam navegar até o pai. A resposta para a relação do projeto dos desenhos <——> não é óbvia para mim. (É uma questão de análise sobre o seu domínio, não uma questão sobre o estilo de codificação).

Há uma diferença notável aqui entre o código OO e, por exemplo, SQL. Em um banco de dados SQL, deve ser a revisiontabela que contém a referência ao seu pai drawing id. No código OO, a classe pai quase sempre contém uma referência aos filhos. Os filhos geralmente não precisam de uma referência aos pais porque a única maneira de você acessá-los é já tendo o pai.

Este artigo é coletado da Internet.

Se houver alguma infração, entre em [email protected] Delete.

editar em
0

deixe-me dizer algumas palavras

0comentários
loginDepois de participar da revisão

Artigos relacionados

Referência circular - questão de arquitetura

Exceção de referência circular

Quão ruim é uma referência circular de serviço?

Referência de nome circular

Como posso evitar uma situação de referência circular

Como corrigir o erro de "Referência circular" no arquivo YAML?

Problema de referência circular com serviço decorado

Mola não resolvível de referência circular Quartz

Questão de arquitetura para evolução de estado

Referência de objeto Java / questão de escopo

Questão de arquitetura de testes de unidade

Usando a instrução ʻimport type` para corrigir o erro de referência de dependência circular

Lambda, SQS e Cloudwatch - questão de arquitetura

Questões de arquitetura Android

Referência circular na contagem de referência Javascript

Exemplo de referência circular de mola

Como posso obter uma referência de um Fragment (SupportMapFragment) carregado pelo NavHostFragment (dos componentes da Arquitetura de Navegação)

Qual é a utilidade de uma referência circular?

EXCEL: Problema de referência circular

Referência circular de modelos em C ++

Arquitetura de classe: dependência circular de classe interna e externa em Python

Referência circular ao tornar o sistema de gerenciamento de estado seguro de tipo

Symfony DI: Referência de serviço circular com assinante de evento Doctrine

Symfony DI: Referência de serviço circular com assinante de evento Doctrine

Como posso localizar uma referência de objeto circular em uma grande coleção de objetos?

Como evito o aviso de referência de argumento circular no suporte ativo

como resolver o problema de "referência circular" do kit de ferramentas redux

Como resolver o problema de "Referência circular detectada para serviço"?

Por que o typescript permite esse tipo de referência circular ao usar tipos genéricos?