окончательный контрольно-пропускной пункт со строителем - PullRequest
1 голос
/ 07 июня 2011

У меня есть график (г), который использует строитель (стиль J Bloch). График необходимо перевернуть для запуска определенной статистики, которая затем кэшируется для доступа к отчетам и алгоритмам анализа.

Итак, график g определяет следующие ссылочные переменные:

private final Builder savedBuilder; // save builder for clone build with same properties.
private final Graph   gPrime;       // must reverse populated graphs BEFORE cache of stats

Примечание: gPrime относится к идентичному графику, за исключением того, что он заполнен из явного g. gPrime.gPrime должен ссылаться на g, так как g - обратная сторона gPrime.

и метод сборки компоновщика:

public Graph build() {
  Graph g = new Graph(this);
  g.gPrime.gPrime = g;
  return g;
}

и конструктор, который берет строителя:

private Graph (Builder builder){  // 'this' used for clarity
  this.gType        = builder.gType;
  this.dropOrphans  = builder.dropOrphans;
  this.fileHandle   = builder.fileHandle;       
  this.nodes        = builder.nodes;            
  this.edges        = builder.edges;            
  this.delimiter    = builder.delimiter;
  this.mapCapacity  = builder.mapCapacity;       
  this.mapLoadFactor    = builder.mapLoadFactor;
  this.savedBuilder = builder;  // save builder for cloning

  emptyGraph();     // build empty structure for data in this graph
  if (this.fileHandle == null) {           // no file data
    if (this.nodes == 0) {         // no sizing info
    ;                  // nothing else to do - - - empty graph
    } else {                    // we have # of nodes
      if ( this.edges == 0) {            // just an edge-less integer graph)
      populateEdgeless(nodes)   ;
      } else {                 // randomly generated graph
    populateRandom(nodes, edges);
      }
    }
  } else {                 // populate from file
    populateFromFile();
  }

  // To create empty graph to transpose our values into,
  // we need to clear out builder settings that would populate a new graph.
  savedBuilder.fileHandle = null;
  savedBuilder.nodes = 0;
  savedBuilder.edges = 0;

  // otherwise, everything the same, so just pass modified builder to constructor
  // save the reference to this graph ( ready for the reversal method to use )
  this.gPrime = new Graph(savedBuilder);

)

Еще раз. Цель состоит из двух графических объектов, каждый из которых ссылается на gPrime.

Последовательность: построить g - - - заполнить g - - - превратить g в пустой граф с теми же характеристиками, что и g

Итак, вот проблема, которую я не совсем понимаю.

Если я назначу g для gPrime.gPrime, либо в сборке после сборки g us, либо в нижней части конструктора, я получу сообщение об ошибке, указывающее, что gPrime является окончательным. Затмение указывает, что это первый gPrime, о котором идет речь, - - что является правдой - - он является окончательным и был назначен. Но gPrime.gprime (с акцентом на второй gPrime) еще не назначен. (Я искал всю программу.)

Я также попытался поместить назначение в конец обратного метода. То же самое.

Я также попробовал g.gPrime.gPrime в компоновщике. То же самое.

Это почти как если бы компилятор не понимал, какой gPrime получает назначение.

Я уверен, что есть что-то, чего я не вижу или не понимаю - - - но - - - - не знаю, как это сделать.

Я могу сделать эту работу, если вычеркну финал, но я пытаюсь добраться до неизменности.

Ответы [ 3 ]

2 голосов
/ 07 июня 2011

Вам нужны циклические зависимости, которые неизменны . Вы должны реализовать это так, чтобы когда вы Build A (в конструкторе A), вы вызывали constructor из B с this.

Вот код с Builders ( Необходимо убедиться, что весь процесс сборки не выходит из текущего потока ):

public class A {

  private final B b_;
  private final String name_;

  private A(Builder b) {
    b_ = b.bB_.a(this).build();
    name_ = b.name_;
  }

  public String name() {
    return name_;
  }

  public B b() {
    return b_;
  }

  @Override
  public String toString() {
    return "[" + name_ + ": " + b_.name() + " ]";
  }

  public static class Builder {

    private B.Builder bB_;
    private String name_;

    public Builder bB(B.Builder bB) {
      bB_ = bB;
      return this;
    }

    public Builder name(String arg) {
      name_ = arg;
      return this;

    }

    public A build() {
      return new A(this);
    }
  }

}

Класс B:

public class B {

  private final A a_;
  private final String name_;

  private B(Builder b) {
    a_ = b.a_;
    name_ = b.name_;
  }

  public String name() {
    return name_;
  }

  @Override
  public String toString() {
    return "[" + name_ + ": " + a_.name() + " ]";
  }

  public static class Builder {

    private A a_;
    private String name_;

    public Builder a(A a) {
      a_ = a;
      return this;

    }

    public Builder name(String arg) {
      name_ = arg;
      return this;

    }

    public B build() {
      return new B(this);
    }
  }

}

Как это использовать:

public class Main {

  public static void main(String[] args) {
    A.Builder aBl = new A.Builder().name("I am A1");
    B.Builder bBl = new B.Builder().name("I am B1");

    A a = aBl.bB(bBl).build();

    System.out.println(a);
    System.out.println(a.b());

  }

}

^
^
^
Если вы хотите иметь один класс и два объекта в круговой зависимости) :

public class A {

  private final A other_;
  private final String name_;

  private A(Builder b) {
    if (b.otherBulder_ != null) {
      other_ = b.otherBulder_.otherInstance(this).build();
    } else {
      other_ = b.otherInstance_;
    }
    name_ = b.name_;
  }

  @Override
  public String toString() {
    return "[" + name_ + ": " + other_.name() + " ]";
  }

  public String name() {
    return name_;
  }

  public A other() {
    return other_;
  }

  static class Builder {

    private Builder otherBulder_;
    private A otherInstance_;
    private String name_;

    Builder name(String name) {
      name_ = name;
      return this;
    }

    Builder otherBuilder(Builder other) {
      otherBulder_ = other;
      return this;
    }

    Builder otherInstance(A instance) {
      otherInstance_ = instance;
      return this;
    }

    A build() {
      return new A(this);
    }
  }

  public static void main(String[] args) {
    Builder a1B = new Builder().name("A1");
    Builder a2B = new Builder().name("A2");

    A a = a1B.otherBuilder(a2B).build();
    System.out.println(a);
    System.out.println(a.other());

  }
}
1 голос
/ 07 июня 2011

Я думаю, что мой ответ - это просто упрощенная версия ответа Оп Де Циркеля с учетом ваших конкретных потребностей. Идея состоит в том, чтобы иметь экземпляр gPrime в компоновщике, поэтому в конструкторе, если в компоновщике есть ненулевой gPrime, используйте его, в противном случае постройте его:

private Graph(Builder builder){


  /* .... setup code omitted....*/

  if (builder.gPrime == null){
     savedBuilder.gPrime = this;
     this.gPrime = new Graph(savedBuilder);
  }else{
     this.gPrime = builder.gPrime;
  }

}

this.gPrime.gPrime = this не будет работать нигде, потому что gPrime является конечным полем экземпляра и может быть инициализирован только при объявлении или в конструкторе для этого экземпляра. this.gPrime.gPrime = this делает инициализацию последнего поля для другого экземпляра, что нарушает "окончательность" gPrime

0 голосов
/ 07 июня 2011

Вы не можете инициализировать неизменную круговую структуру.

Вы можете сделать его практически неизменным, изменив gPrime на final и убедившись, что всегда устанавливаете его на значение перед его использованием.

Благодаря ответу Оп де Циркеля:

Вы можете использовать два компоновщика или переключатель в функциональности вашего компоновщика, где у вас есть компоновщик, который содержит ссылку на объект Graph, который вы в данный момент создаете:

public class Graph {

    private final Graph gPrime;
    private final String name_;

    private Graph(PrimeBuilder b) {
        gPrime = b.g;
        name_ = b.name_;
    }

    private Graph(Builder b) {
        gPrime = new PrimeBuilder(this).name("gPrime").build();
        name_ = b.name_;
    }

    public String name() {
        return name_;
    }

    public Graph gPrime() {
        return gPrime;
    }

    @Override
    public String toString() {
        return "I am " + name_ + ", my gPrime is " + gPrime.name();
    }

    public static class PrimeBuilder {

        private Graph g;
        private String name_;

        public PrimeBuilder(Graph g) {
            this.g = g;
        }

        public PrimeBuilder name(String arg) {
            name_ = arg;
            return this;

        }

        public Graph build() {
            return new Graph(this);
        }
    }

    public static class Builder {

        private String name_;

        public Builder name(String arg) {
            name_ = arg;
            return this;
        }

        public Graph build() {
            return new Graph(this);
        }
    }

}

Пример использования:

public class Main {

  public static void main(String[] args) {
    Graph g = new Graph.Builder().name("g").build();

    System.out.println(g);
    System.out.println(g.gPrime());

  }

}
...