Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/381.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何为不可变类实例之间的循环建模?_Java_Class Design - Fatal编程技术网

Java 如何为不可变类实例之间的循环建模?

Java 如何为不可变类实例之间的循环建模?,java,class-design,Java,Class Design,不可变类很好,但有一个大问题我想不出一个合理的方法来解决——循环 class Friend { Set<Friend> friends(); } 班友{ 设置朋友(); } 一个人怎么能把你当作朋友,反过来又把我当作朋友呢 不变性 这个来自外部世界的类肯定是不可变的。为了进行相等性检查,内部保存的值应该是常量。不需要编译器强制执行不变性,就可以在体系结构上有效。您可以拥有一个合法的不可变对象,该对象接受构造后初始化参数。例如 private Object something

不可变类很好,但有一个大问题我想不出一个合理的方法来解决——循环

class Friend {
   Set<Friend> friends();
}
班友{
设置朋友();
}
一个人怎么能把你当作朋友,反过来又把我当作朋友呢

不变性
这个来自外部世界的类肯定是不可变的。为了进行相等性检查,内部保存的值应该是常量。

不需要编译器强制执行不变性,就可以在体系结构上有效。您可以拥有一个合法的不可变对象,该对象接受构造后初始化参数。例如

private Object something;

public void init( final Object something )
{
   if( this.something != null )
   {
       throw new IllegalStateException();
   }

   this.something = something
}
成员字段“something”不是最终字段,但也不能设置多次

一个更复杂的变体基于评论中的讨论

private boolean initialized;
private Object a;
private Object b;

public void init( final Object a, final Object b )
{
   if( this.initialized )
   {
       throw new IllegalStateException();
   }

   this.initialized = true;
   this.a = a;
   this.b = b;
}

public Object getA()
{
   assertInitialized();
   return this.a;
}

public Object getB()
{
   assertInitialized();
   return this.b;
}

private void assertInitialized()
{
   if( this.initialized )
   {
       throw new IllegalStateException( "not initialized" );
   }
}

[[[编辑:添加代码以演示完全不可变的概念]]

这就是为什么构建器非常适合不可变的原因——它们允许在构建过程中的可变性,以便在“冻结”它之前将所有内容都设置好。在这种情况下,我想您需要一个支持创建循环的好友生成器

final FriendBuilder john = new FriendBuilder().setName("john");
final FriendBuilder mary = new FriendBuilder().setName("mary");
final FriendBuilder susan = new FriendBuilder().setName("susan");
john
  .likes(mary)
  .likes(susan);
mary
   .likes(susan)
   .likes(john);
susan
   .likes(john);

// okay lets build the immutable Friends
Map<Friend> friends = FriendsBuilder.createCircleOfFriends(john, mary, susan);
Friend immutableJohn = friends.get("john");

建立循环模型的正确方法是使用。一个源代码行注释就足以强制执行不可变性:“”

你想要什么样的强制执行?你想让一只迅猛龙出现吗?
mutable
inmutable
之间的区别只是一种惯例。然而,RAM上的位可以很容易地修改,并且可以打破任何封装和数据隐藏约定

暂时忽略velociraptor,Java不支持inmutable类型。作为一种变通方法,您需要对行为类似的数据类型进行建模

为了使inmutable属性有意义,您需要创建一个
Friend
接口,具有一个实现类:
InmutableFriend
,并且对象的构造应该完全发生在构造函数内部

然后,因为图包含循环,所以在创建最终的不可变实例之前,您需要将图节点存储在一些可变的临时结构中。您还需要在
InmutableFriend.friends()方法上返回一个

最后,要克隆图,您需要在可变图上实现类似的算法。然而,有一个问题是,当图形不存在时会发生什么

接口朋友{
公开设置好友();
}
类可变朋友{
私有集关系=新HashSet();
无效连接(可变朋友或其他朋友){
如果(!relations.contains(otherFriend)){
关系。添加(其他域);
连接(这个);
}
}
朋友冻结{
映射表=。。。;
/*
*FIXME:实施广度优先搜索以克隆图形,
*将此节点用作起点。
*
*TODO:如果图形未连接,则此操作将不起作用。
*
*/
}
}
类InmutableFriend()实现Friend{
专用集连接;
公共集友(){
返回连接;
}
公共InmutableFriend(设置连接){
//我不能碰这个。
this.connections=Collections.unmodifiableSet(连接);
}
}

您是否提前知道所需的所有双向关系,还是一次添加一个?在后一种情况下,实际上没有办法获得不变性保证,因为您确实在更改对象。在这种情况下,不,我试图保持简单。我想您的意思是:if(something==null)实际上,我的意思是“if(this.something!=null)”。if语句用于捕获多次设置某个内容的尝试。与此相关的部分包括:(1)此“私有”init方法可能永远不会被错误调用,(2)如果
某个内容的有效值为
null
,则此代码将不起作用。如果
null
something
的有效值,则需要在一个单独的标志中跟踪
something
的初始化。@Bert-(1)如果类未初始化,则类中的其他方法会失败,这很容易解决;(2) 您已经指定了解决方案。在该模式的更复杂版本中,我通常会看到“已初始化”布尔成员跟踪此状态,因为通常有多个字段正在初始化。@Konstantin-(1)其绝对可寻址,但代价是更复杂-在每个方法中执行am-I-完全初始化检查。这就是我在可能的情况下倾向于避开这种“部分初始化”模式的原因之一。(2)我认为值得指出的是空问题和解决方案,所以在实践中使用这种模式的人会考虑这种情况。请注意,构造器模式隐藏了一个事实,即您正在进行后期构造初始化。Friend类不能有一个完整的最终结构来保存friends。@Konstantin Komissarchik您可以使用堆栈上的多个friends(最好情况下是图的直径,最坏情况下都是图的直径)来执行此操作,但仍然保持不变性。@Konstantin Komissarchik
class a{final B;a(){this.B=new B(this);}}class B{final a;B(a){this.a=a;}}
我相信这是函数式语言中众所周知的东西。@bert这可能是唯一的方法。字段不是最终的,但来自Friend外部实例的字段看起来是不可变的。将保持此字段打开一段时间以收集更多的意见。我想构建器的interning+lookup功能是拥有类似singleton的Friend实例的最好方法s、 @Stephen C当然可以,但这个评论可能有点小。冰箱模式真的很难看,我开始认为Friend看起来是不可变的,但在构建器的深处,它会添加sttuff,然后冻结,在这一点上,它可以提供给外部世界…@mP:冰箱不是一个模式,它是一个叫做deep Copy:sor的算法我误读了M
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;

/**
 * Note: potentially cycle graph - be careful of deep equals/hashCode/toString/etc.
 * Immutable
 */
public class Friend {

    public static class Builder {

        private final String name;
        private final Set<Builder> friends =
            new HashSet<Builder>();

        Builder(final String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public Set<Builder> getFriends() {
            return friends;
        }

        void likes(final Builder... newFriends) {
            for (final Builder newFriend : newFriends)
            friends.add(newFriend);
        }

        public Map<String, Friend> createCircleOfFriends() {
            final IdentityHashMap<Builder, Friend> existing =
                new IdentityHashMap<Builder, Friend>();

            // Creating one friend creates the graph
            new Friend(this, existing);
            // after the call existingNodes contains all the nodes in the graph

            // Create map of the all nodes
            final Map<String, Friend> map =
                new HashMap<String, Friend>(existing.size(), 1f);
            for (final Friend current : existing.values()) {
                map.put(current.getName(), current);
            }

            return map;
        }
    }

    final String name;
    final Set<Friend> friends;

    private Friend(
            final Builder builder,
            final Map<Builder, Friend> existingNodes) {
        this.name = builder.getName();

        existingNodes.put(builder, this);

        final IdentityHashMap<Friend, Friend> friends =
            new IdentityHashMap<Friend, Friend>();
        for (final Builder current : builder.getFriends()) {
            Friend immutableCurrent = existingNodes.get(current);
            if (immutableCurrent == null) {
                immutableCurrent =
                    new Friend(current, existingNodes);
            }
            friends.put(immutableCurrent, immutableCurrent);
        }

        this.friends = Collections.unmodifiableSet(friends.keySet());
    }

    public String getName() {
        return name;
    }

    public Set<Friend> getFriends() {
        return friends;
    }


    /** Create string - prints links, but does not traverse them */
    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer();
        sb.append("Friend ").append(System.identityHashCode(this)).append(" {\n");
        sb.append("  name = ").append(getName()).append("\n");
        sb.append("  links = {").append("\n");
        for (final Friend friend : getFriends()) {
            sb
            .append("     ")
            .append(friend.getName())
            .append(" (")
            .append(System.identityHashCode(friend))
            .append(")\n");
        }
        sb.append("  }\n");
        sb.append("}");
        return sb.toString();
    }

    public static void main(final String[] args) {
        final Friend.Builder john = new Friend.Builder("john");
        final Friend.Builder mary = new Friend.Builder("mary");
        final Friend.Builder susan = new Friend.Builder("susan");
        john
          .likes(mary, susan);
        mary
           .likes(susan, john);
        susan
           .likes(john);

        // okay lets build the immutable Friends
        final Map<String, Friend> friends = john.createCircleOfFriends();

        for(final Friend friend : friends.values()) {
            System.out.println(friend);
        }

        final Friend immutableJohn = friends.get("john");
    }
}
Node 11423854 {
  value = john
  links = {
     susan (19537476)
     mary (2704014)
  }
}
Node 2704014 {
  value = mary
  links = {
     susan (19537476)
     john (11423854)
  }
}
Node 19537476 {
  value = susan
  links = {
     john (11423854)
  }
}
interface Friend {
    public Set<Friend> friends();
}

class MutableFriend {
    private Set<MutableFriend> relations = new HashSet<MutableFriend>();

    void connect(MutableFriend otherFiend) {
        if (!relations.contains(otherFriend)) {
            relations.add(otherFiend);
            otherFriend.connect(this);
        }
    }

    Friend freeze() {
        Map<MutableFriend, InmutableFriend> table = ...;

        /*
         * FIXME: Implement a Breadth-first search to clone the graph,
         * using this node as the starting point.
         *
         * TODO: If the graph is not connected this won't work.
         *
         */
    }
}

class InmutableFriend() implements Friend {
    private Set<Friend> connections;

    public Set<Friend> friends() {
        return connections;
    }

    public InmutableFriend(Set<Friend> connections) {
        // Can't touch this.
        this.connections = Collections.unmodifiableSet(connections);
    }
}