|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 7
While atomic code may require other libraries, its execution is self-contained -- that is, it contains no call-level interdependencies. If there is interdependency across individual method calls, then
your individual methods are not atomic, though they may form an atomic operation all together. Consider the simple example
in Listing 1. A call on the setRadius() method must precede a call on any of the computational methods, such as getDiameter(). Therefore, setRadius() and getDiameter() are not atomic separately. Though this is fine from a purely object-oriented design standpoint, it has implications in distributed
and parallel environments. The computational methods in Listing 1 cannot be processed in parallel across distributed workers
or cores independent of setRadius().
package com.appistry.samples;
public class NonAtomicCircle {
private int radius;
public void setRadius(int radius) {
this.radius = radius;
}
// setRadius must be called before any of the following
public float getArea() {
return 3.14f * (radius * radius);
}
public int getDiameter() {
return 2 * radius;
}
public float getCircumference() {
return 3.14f * getDiameter();
}
}
The code in Listing 2, though more static in nature, does not have this dependency or its associated restrictions, and can be executed independently. In more complex classes involving equally complex state, this can become a critical design consideration.
package com.appistry.samples;
public class AtomicCircle {
private int radius;
public void setRadius(int radius) {
this.radius = radius;
}
// These computational methods can called independently
// and distributed or run in parallel.
public static float getArea(int radius) {
return 3.14f * (radius * radius);
}
public static int getDiameter(int radius) {
return 2 * radius;
}
public static float getCircumference(int radius) {
return 3.14f * getDiameter(radius);
}
}
Atomic code is also concise by its nature, and, as stated above, has a specific, clearly defined purpose. Fat, hairy, do-multiple-things methods like
provisionLineItem() in Listing 3 are likely not atomic (though the methods it delegates to are candidates). If a method serves multiple purposes,
then you should break those purposes up into separate atomic methods, and likely into separate classes, as reserveInventory() and calculateWeight() sound like separate concerns.
package com.appistry.samples;
public class OverAchiever {
public void provisionLineItem(Shipment shipment, String sku, int quantity) {
Product item = reserveInventory(sku, quantity);
float weight = calculateWeight(item, quantity);
shipment.add(item, quantity, weight);
reportPopularityToCorporate(sku, quantity);
}
}
The same holds true for long-running methods: they are not usually atomic. Long-running code may have a single purpose, but if you can break the method down into more atomic steps, then you gain flexibility when running that code in cloud environments or on multiple cores.