简体   繁体   中英

Immutability with inheritance design pattern

I'm implementing some code in Scala, following a tutorial in Java.

The tutorial has a piece of code that can be minimized into the following:

public abstract class A {
    private int id;

    public A(String name) {
        id = Helper.getId(name);
        fetchAllAttributes();
    }

    protected abstract void fetchAllAttributes();

    protected int fetchAttribute(String attribute) {
        Helper.getAttribute(id, attribute);
    }

    public class B extends A {
        int volume;

        public B() {
            super("B");
        }

        @Override
        protected void fetchAllAttributes() {
            volume = super.fetchAttribute("volume");
        }

    }
}

And this is what it looks like when you translate it into Scala:

abstract class A(val name: String) {
  private val id = Helper.getId(name)
  fetchAllAttributes()

  protected def fetchAllAttributes(): Unit

  protected def fetchAttribute(attribute: String): Int = {
    Helper.getAttribute(id, attribute)
  }

  class B() extends A("B") {
    var volume = 0

    override protected def fetchAllAttributes(): Unit = {
      volume = super.fetchAttribute("volume")
    }
  }

}

And here I'd like to ask two questions:

  1. Is there a name for this pattern, where you call an abstract method in the abstract class' constructor and provide a method for the child classes to use in their implementation?
  2. As a Scala developer, I really don't like mutable objects and variables. Is there a good way to implement this in an immutable fashion, using only val s?
  1. You can call it an anti-pattern. See previous discussions on SO about calling overridable methods from a constructor.
  2. When refactoring java code to scala code then what helped me the most in the beginning was understanding what are methods with side effects. You don't want to have methods in classes that depend on mutable fields and/or change them. (Like your class B has with that volume) This small principle helps keep the classes clear from mutable state. This is not a rule which is set to stone though. With akka actors it is not uncommon to have mutable state for example.

A quote from scala tutorial ( Source ):

Again, this means that a function should not have any side effects. It should take input variables, not modify those variables, then calculate and return something new. Unlike object-oriented programming, you should not change (mutate) the state of other variables, even if those variables are (a) variables of your class or (b) variables that were passed in to your function/method.

I don't know of a name for this pattern. It's hard to know exactly how to translate it into immutable style without knowing the goal of the pattern, but here's something similar, if you squint hard enough:

class FactoryFromAttributes[A](name: String) {
  def construct(f: (String => Int) => A): A = {
    val id = Helper.getId(name)
    f(str => Helper.getAttribute(id, str))
  }
}

class B(volume: Int)

object B extends FactoryFromAttributes[B]("B") {
  def apply(): B = construct { fetchAttribute =>
    new B(fetchAttribute("volume"))
  }
}

To me, this kind of logic makes more sense in a companion object, so I moved it to there, rather than keep an odd subclass relationship in the resulting constructed class. Initialization logic outside of constructors rarely lends itself to good immutable style.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM