2015-09-24 3 views
-1

Что следует учитывать, чтобы сделать изменяемый класс неизменным? Например: Можем ли мы иметь методы push и pop в классе неизменяемых стеков? Или мы просто должны удалить любой метод, который изменяет состояние объекта-объекта?Mutable vs. Immutable

+3

Если вы можете нажать новый элемент в стек, это не неизменная. –

+0

Почему размер '' '' '' растет, если вы только изменили массив 'newElement'? –

+1

Вы хотите изменить состояние объекта _immutable_? Как и почему? – Tom

ответ

1

Суть: Вы бы лучше, чтобы удалить метод, изменяющее состояние экземпляра объекта из класса. Но если вы хотите сохранить его, тогда он должен создать новый объект с другим состоянием из исходного и вернуть новый объект обратно.

Вот более конкретный ответ:

Там не должно быть никаких методов изменения состояния объекта в классе неизменяемых.

Существует много методов пустот, которые изменяют состояние этого объекта в изменяемом классе. Поэтому мы должны изменить их подписи таким образом, чтобы вернуть новый объект вместо изменения состояния этого «объекта».

Также существует множество непустых методов, которые изменяют состояние этого «объекта» и возвращают значение, которое они изменили в «этом». Подпись этих методов также должна быть изменена таким образом, что они возвращают новый объект вместо изменения состояния «this». Говоря о списках, обычно требуется другой метод (например, «peek»), чтобы получить определенное значение. Проверьте образец ниже, чтобы получить то, что я имею в виду:

Проверьте этот «толчок» и «поп» методы изменяемого класса стека:

public class Stack <E> { 
… 
public void push (E e) { 
    ensureCapacity(); // This method checks for capacity 
    elements[size++] = e; 
    } 

Этот метод добавляет новый элемент в верхней части стек и изменяет состояние этого «объекта» таким образом.

public E pop() { 
    if (size == 0) throw new IllegalStateException("Stack.pop"); 
    E result = elements[--size]; 
    elements[size] = null; 
    return result; 
    } 
… 
} 

Этот метод удаляет элемент в верхней части стека и возвращает его обратно, и изменяет состояние «этого» объекта путем удаления элемента.

Теперь предположим, что нам нужно изменить эти методы, чтобы сделать этот стек неизменным.Давайте сначала рассмотрим метод «push»:

«push» - это метод пустоты, который изменяет состояние «этого» объекта, добавляя к нему новый элемент. Для того, чтобы стек неизменны мы создадим новый стек, похожий на «это» и добавить новый элемент в новом стеке и вернуть его обратно:

public class ImmStack <E> { 
... 
    /** 
    * this method pushes the item to a new object and keeps the original object unchanged 
    * @param e The item to be pushed 
    * @return A new list 
    * @PRE An original list object is required as well as an item to be pushed 
    * @POST A new list would be returned 
    */ 
    @SuppressWarnings({ "unchecked", "rawtypes" }) // All items in this.elements[] are of type E 
    public ImmStack<E> push (E e) { 

     ImmStack<E> stc = new ImmStack(getNewElements());  
     stc.elements=ensureCapacity(stc.elements); 
     stc.elements[size] = e; 
     stc.size = size +1;   
     return stc; 
     } 

метод «поп» изменяет состояние «этого» объекта, удаление элемента. Для того, чтобы класс неизменяемыми мы reate новый стек, похожий на «это» и удалить элемент из этого нового стека и вернуть его обратно:

/** 
    * This pop method returns a new stack without the element at the top of the original list 
    * @return The new stack 
    * @POST The new stack would be returned 
    */ 
    @SuppressWarnings({ "unchecked", "rawtypes" }) // All items in this.elements[] are of type E 
    public ImmStack<E> pop() { 

     if (size == 0) throw new IllegalStateException("Stack.pop"); 

     ImmStack<E> stc = new ImmStack(getNewElements());   
     stc.elements=ensureCapacity(stc.elements); 
     stc.elements[size-1] = null; 
     stc.size=size-1; 
     return stc; 
     } 

старый «поп» метод возвращал элемент в верхней части. Мы также нуждаемся в новом методе, который возвращает элемент вверху, чтобы покрыть эту функцию:

/** 
    * Returns item at front of queue without removing. 
    * @return item at front 
    * @throws java.util.NoSuchElementException if empty 
    */ 
    public E top() 
    { 
    if (this.isEmpty()) 
     { 
     throw new NoSuchElementException("Queue underflow"); 
     } 
     return elements[size-1]; 
    } 

Это был просто пример. У вас может быть больше методов для изменения в вашем классе, чтобы сделать его неизменным.

1

Если ваш стек неизменен, то по определению его нельзя изменить. Методы push() и pop() не могут быть выполнены.

Если метод не может быть успешно завершен, вы можете создать исключение. Когда метод может никогда не будет выполнен успешно, стандартное исключение для броска - UnsupportedOperationException.

Например:

public E[] push (E e) { 
    throw new UnsupportedOperationException(); 
} 

EDIT:

Вы отмечаете в комментариях, что ваш метод толчка() только возвращающиеся глубокую копию стеки с новым элементом. Похоже, вы представляете неизменяемый стек как экземпляр класса, а толкаемый стек - как массив.

Вы можете получить размер одного из двух массивов, на который ссылается newElements, с newElements.length. Таким образом, вы могли бы написать такой код:

public E[] push (E e) { 
    E[] newElements=getNewElements(); 
    int oldLength = newElements.length; 
    newElements=ensureCapacity(newElements); 
    int lastIndexInNewArray = oldLength; 
    newElements[ lastIndexInNewArray ] = e; 
    return newElements; 
} 
0

Ниже приведена реализация неизменяемого стека в C#.

Нажатие и выталкивание возвращает вам совершенно новый стек, и Peek позволяет вам взглянуть на верхнюю часть стека, не выталкивая его.
Обратите внимание, что копирование всего стека не требуется.

Вот как неизменяемые структуры реализуются в любых нетривиальных случаях. В некоторых случаях очень важны нетривиальные неизменные структуры. Плакаты, говорящие, что это невозможно сделать, сильно дезинформированы.

Исходный код и более подробную информацию можно найти здесь:
https://blogs.msdn.microsoft.com/ericlippert/2007/12/04/immutability-in-c-part-two-a-simple-immutable-stack/

public interface IStack<T> : IEnumerable<T> 
{ 
    IStack<T> Push(T value); 
    IStack<T> Pop(); 
    T Peek(); 
    bool IsEmpty { get; } 
} 

public sealed class Stack<T> : IStack<T> 
{ 
    private sealed class EmptyStack : IStack<T> 
    { 
     public bool IsEmpty { get { return true; } } 
     public T Peek() { throw new Exception("Empty stack"); } 
     public IStack<T> Push(T value) { return new Stack<T>(value, this); } 
     public IStack<T> Pop() { throw new Exception("Empty stack"); } 
     public IEnumerator<T> GetEnumerator() { yield break; } 
     IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } 
    } 
    private static readonly EmptyStack empty = new EmptyStack(); 
    public static IStack<T> Empty { get { return empty; } } 
    private readonly T head; 
    private readonly IStack<T> tail; 
    private Stack(T head, IStack<T> tail) 
    { 
     this.head = head; 
     this.tail = tail; 
    } 
    public bool IsEmpty { get { return false; } } 
    public T Peek() { return head; } 
    public IStack<T> Pop() { return tail; } 
    public IStack<T> Push(T value) { return new Stack<T>(value, this); } 
    public IEnumerator<T> GetEnumerator() 
    { 
     for(IStack<T> stack = this; !stack.IsEmpty ; stack = stack.Pop()) 
      yield return stack.Peek(); 
    } 
    IEnumerator IEnumerable.GetEnumerator() {return this.GetEnumerator();} 
} 
Смежные вопросы