Asked  7 Months ago    Answers:  5   Viewed   30 times

What's a "static factory" method?

 Answers

50

We avoid providing direct access to database connections because they're resource intensive. So we use a static factory method getDbConnection that creates a connection if we're below the limit. Otherwise, it tries to provide a "spare" connection, failing with an exception if there are none.

public class DbConnection{
   private static final int MAX_CONNS = 100;
   private static int totalConnections = 0;

   private static Set<DbConnection> availableConnections = new HashSet<DbConnection>();

   private DbConnection(){
     // ...
     totalConnections++;
   }

   public static DbConnection getDbConnection(){

     if(totalConnections < MAX_CONNS){
       return new DbConnection();

     }else if(availableConnections.size() > 0){
         DbConnection dbc = availableConnections.iterator().next();
         availableConnections.remove(dbc);
         return dbc;

     }else {
         throw new NoDbConnections();
     }
   }

   public static void returnDbConnection(DbConnection dbc){
     availableConnections.add(dbc);
     //...
   }
}
Tuesday, June 1, 2021
 
Owen
answered 7 Months ago
64

"Static" and "dynamic" aren't the right descriptions for that.

-> indicates a instance functions or instance data, meaning that the function or data has an implicit $this reference. To put it another way you're referring to the function or variable within a particular object.

:: indicates a class function or class variable. This is very similar to a global function or variable in that there is no implicit $this reference. All instance of that class share that function or variable.

"Dynamic" would be a more accurate description for, say, PHP overloading where you can "dynamically" create variables, for example, using the magic methods __get() and __set() (which are called when you try to access a property that can't be found; you can overload these methods to essentially pretend the requested member exists).

Saturday, May 29, 2021
 
KouiK
answered 7 Months ago
86

Note 1: To prevent the use of raw-types, I have opted to provide a generic type for each class that I mention below by using E, representing an element of a Collection<E>.

Note 2: This answer is subject to change; please edit this post if a typo has occurred.

What are collection factory methods?

A collection factory method in Java is a static method that provides a simple way of initializing an immutable Collection<E>.

Being immutable, no elements can be added to, removed from, or modified inside the Collection<E> after it is initialized.

With Java 9, collection factory methods are provided for the following interfaces: List<E>, Set<E>, and Map<K, V>

What do they improve?

Up until Java 9, there has been no simple, universal method to initialize a Collection<E> with initial elements/key-value entries. Previously, developers were required to initialize them as follows (assuming the generic types E, K, and V have been replaced with Integer):

  • List<Integer>
    • The following method is arguably the simplest to initialize a List<Integer> with initial elements, however the result is simply a view of a List<Integer>; we are unable to add to or remove from this List<Integer>, but we are still able to modify existing elements by using List#set.
      • List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
    • If we wanted our List<Integer> to be entirely mutable, then we would have to pass it to the constructor of an ArrayList<Integer>, for example:
      • List<Integer> mutableList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
  • Set<Integer>
    • A Set<Integer> required more code to initialize with initial elements than a List<Integer> does (seeing as a List<Integer> is required to initialize a Set<Integer> with initial elements), which can be seen below.
      • Set<Integer> mutableSet = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
  • Map<Integer, Integer>
    • A Map<Integer, Integer> is arguably the most complicated to initialize with initial key-value entries; however, there are multiple ways to go about it.
      • One method was to first initialize an empty Map<Integer, Integer> and simply call Map#put to add key-value entries.
      • Another method was to use an anonymous class with two curly braces, which would still require Map#put to be called.

Why should I use them?

I argue that collection factory methods provide the developer with a concise method of initializing a List<E>, Set<E>, or Map<K, V> with initial elements/key-value entries, which can be seen by the examples below.

What is the proper syntax to use?

For simplicity, these examples will replace the generic types E, K, and V with Integer.

  • List<Integer>
    • List<Integer> list = List.of();
      • Initializes an empty, immutable List<Integer>.
    • List<Integer> list = List.of(1);
      • Initializes an immutable List<Integer> with one element.
    • List<Integer> list = List.of(1, 2);
      • Initializes an immutable List<Integer> with two elements.
    • List<Integer> list = List.of(1, 2, 3, 4, 5, ...);
      • Initializes an immutable List<Integer> with a variable amount of elements.
  • Set<Integer>
    • Set<Integer> set = Set.of();
      • Initializes an empty, immutable Set<Integer>.
    • Set<Integer> set = Set.of(1);
      • Initializes an immutable Set<Integer> with one element.
    • Set<Integer> set = Set.of(1, 2);
      • Initializes an immutable Set<Integer> with two elements.
    • Set<Integer> set = Set.of(1, 2, 3, 4, 5, ...);
      • Initializes an immutable Set<Integer> with a variable amount of elements.
  • Map<Integer, Integer>
    • Map<Integer, Integer> map = Map.of();
      • Initializes an empty, immutable Map<Integer, Integer>.
    • Map<Integer, Integer> map = Map.of(1, 2);
      • Initializes an immutable Map<Integer, Integer> with one key-value entry.
      • Note that the key is 1 and the value is 2.
    • Map<Integer, Integer> map = Map.of(1, 2, 3, 4);
      • Initializes an immutable Map<Integer, Integer> with two key-value entries.
      • Note that the keys are 1 and 3 and the values are 2 and 4.
    • Map<Integer, Integer> map = Map.ofEntries(Map.entry(1, 2), Map.entry(3, 4), ...);
      • Initializes an immutable Map<Integer, Integer> with a variable amount of key-value entries.

As you can see, this new method of initialization requires less code than its predecessors.

Can I use collection factory methods to create mutable objects?

The Collection<E> created by collection factory methods are inherently immutable, however we are able to pass them to a constructor of an implementation of the Collection<E> to produce a mutable version:

  • List<Integer>
    • List<Integer> mutableList = new ArrayList<>(List.of(1, 2, 3, 4, 5));
  • Set<Integer>
    • Set<Integer> mutableSet = new HashSet<>(Set.of(1, 2, 3, 4, 5));
  • Map<Integer, Integer>
    • Map<Integer, Integer> mutableMap = new HashMap<>(Map.of(1, 2, 3, 4));
Friday, August 13, 2021
 
Max Yankov
answered 4 Months ago
62

You have two options:

1) Abstract Factory:

RectangularShape extends Shape

RoundShape extends Shape

and RectangularShapeFactory and RoundShapeFactory

2) Builder (see also Item 2 in Effective Java)

public Shape {
    private final int x;
    private final int y;
    private final double radius;

    private Shape(Builder builder) {
        x = builder.x;
        y = builder.y;
        radius = builder.radius;
    }

    public static class Builder {
        private final int x;
        private final int y;
        private double radius;

        public Builder(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public Builder radius(double radius) {
            this.radius = radius;
            return this;
        }

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

//in client code 

    Shape rectangle = new Shape.Builder(x,y).build();
    Shape circle = new Shape.Builder(x,y).radius(radiusValue).build();
Saturday, September 4, 2021
 
Jan Gassen
answered 3 Months ago
37

I finally went for the suggestion provided by @Ray Tayek in a comment to the original question. I simply pass in an instance of the NoteDataStore at the time of creating the BusinessLogicLayer.

This simple solution suits my needs pretty well since I do not really require a Factory. My main objective was for the BL Layer to be unaware of the exact implementation classes of the interfaces it uses. Now, instead of a Factory, it is the core "Controller" layer that creates a concrete implementation of the interfaces and supplies them to the BL Layer. This is just perfect!

Here is the code snippet.

public class NoteDataStoreAndroid implements NoteDataStore{

    public boolean deleteNote(long noteId){
        /*
         * Android-specific database code
         */
    }

    /* ... similarly, other methods */
}


public class AndroidCoreApp{

    public void doBusinessLogic(){
        BusinessLogicLayer businessLogic = new BusinessLogicLayer(new NoteDataStoreAndroid());
        businessLogic.startBusinessLogic();
    }
}

public class BusinessLogicLayer {

    private NoteDataStore mDataStore;
    public BusinessLogicLayer(NoteDataStore dataStore){
        this.mDataStore = dataStore;

        //Do something useful with mDataStore
    }

    public void startBusinessLogic(){
        //Do something useful with mDataStore
    }

}
Wednesday, November 10, 2021
 
Valdas
answered 3 Weeks ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share