I was wondering what is the correct way to organize my class hierarchy in the following situation.
I wanted to build an abstraction around postgresql advisory lock.
Note just for context: An advisory lock is a lock that you can obtain at a session or transaction level. Postgres handle all the complexity for you.
The code that I've written so far is something like
interface DBLockService
interface SessionLockService : DBLockService {
fun acquire(id: Long)
fun unlock(id: Long): Boolean
}
interface TransactionalLockService : DBLockService {
fun txAcquire(id: Long)
}
abstract class BaseDBLockService(protected val entityManager: EntityManager): DBLockService {
protected fun executeAcquire(preparedStatement: String, id: Long) {
executeAcquire<Any>(preparedStatement, id)
}
protected inline fun <reified T> executeAcquire(preparedStatement: String, id: Long) =
entityManager
.createNativeQuery(preparedStatement, T::class.java)
.setParameter("id", id)
.singleResult as T
}
#Component
class LockServiceImpl(
entityManager: EntityManager
) : BaseDBLockService(entityManager),
SessionLockService {
companion object {
const val acquireStatement = "SELECT pg_advisory_lock(:id)"
const val unlockStatement = "SELECT pg_advisory_unlock(:id)"
}
override fun acquire(id: Long) {
executeAcquire(acquireStatement, id)
}
override fun unlock(id: Long) =
executeAcquire<Boolean>(unlockStatement, id)
}
#Component
class TransactionalLockServiceImpl(
entityManager: EntityManager
) : BaseDBLockService(entityManager),
TransactionalLockService {
// very similar implementation
}
Looking at this code there is something that tell's me that there is something wrong:
DBLockService is a bit useless interface, there is no method
Are SessionLockService and TransactionalLockService just an implementation detail? Is it correct that there is a different interface for every "type" of lock?
But at the same time, if I remove the DBLockService seems very odd to me that there are 2 interfaces (SessionLockService and TransactionalLockService) with very similar context that are not related in any way.
Moreover, removing DBLockService, I'll have the 2 implementations (LockServiceImpl and TransactionalLockServiceImpl) that extends from the abstract class BaseDBLockService to implement these 2 interfaces but at the same time the abstract class is not related to them.
What to you think?
Thanks
Update
As requested I'll add an example of a real case scenario
#Service
class SomethingService(private val lockService: TransactionalLockService){
#Transactional
fun aMethod(entityId: Long){
lockService.txAcquire(entityId)
//code to be synchronized or there will be problems
}
}
I would like to inject a class of a generic LockService but I cannot find a way to abstract that because imho a lock that disappear after the transaction ends is a lock different from a lock that disappear after the connection to the db is closed (session lock) that is different from a lock that need to be unlocked automatically.
It's possible that there are a lot of other implementations of lock, for example a TimeoutLock that remove the lock after some time.
But I'm not able to think how to separate these implementation details from the general concept of a Lock.
Okay, thanks for the example. I still find it a bit odd to call what you want to implement a Service. I'd probably call it a Strategy, but that's not really that important. It's just a semantic preference.
Anyway, what I would do is probably something like the following (untested/pseudo code):
interface LockService {
Boolean acquire(Long id);
Boolean unlock(Long id);
}
abstract class BaseLockService
implements LockService {
protected EntityManager entityManager;
BaseLockService(EntityManager entityManager) {
this.entityManager = entityManager;
}
protected Boolean executeAcquire(String preparedStatement, Long id) {
// your generic implementation
}
}
class SessionLockService
extends BaseLockService {
private static class Statements {
static final String acquireStatement = "...";
static final String unlockStatement = "...";
}
SessionLockService(EntityManager entityManager) {
super(entityManager);
}
#Override
Boolean acquire(Long id) {
return executeAcquire(Statements.acquireStatement, id);
}
#Override
Boolean unlock(Long id) {
return executeAcquire(Statements.unlockStatement, id);
}
}
class TransactionalLockService
extends BaseLockService {
private static class Statements {
static final String acquireStatement = "...";
}
TransactionalLockService(EntityManager entityManager) {
super(entityManager);
}
#Override
Boolean acquire(Long id) {
return executeAcquire(Statements.acquireStatement, id);
}
#Override
Boolean unlock(Long id) {
// simply return true
return true;
// or if there's some Postgres or EntityManager mechanism to find out if the transaction is still active:
return !entityManager.isInTransaction(id);
}
}
class SomeService {
private final LockService lockService;
SomeService(LockService lockService) {
this.lockService = lockService;
}
void aMethod(Long entityId) {
if(!lockService.acquire(entityId)) {
throw new SomeException();
}
// do code that needs lock
if(!lockService.unlock(entityId)) {
throw new SomeException();
}
}
}
So basically, I would use a common interface and just make TransactionalLockService.unlock() sort of a no-op function that always returns true or, if achievable, more desirable: return the result of some probe mechanism to find out if the transaction with id has correctly ended.
Another idea would be to have a Lock interface, that a LockService returns (very abbreviated example):
interface Lock {
Boolean unlock();
}
interface LockService {
Lock acquire(Long id);
}
class TransactionalLock
implements Lock {
private Long id;
TransactionalLock(Long id) {
this.id = id;
}
#Override
Boolean unlock() {
// again, either simply return true
return true;
// ...or some result that verifies the transaction has ended
return verifyTransactionHasEnded(id);
}
}
class SomeService {
private final LockService lockService;
SomeService(LockService lockService) {
this.lockService = lockService;
}
void aMethod(Long entityId) {
Lock lock = lockService.acquire(entityId);
if(lock == null) {
throw new SomeException();
}
// do code that needs lock
if(!lock.unlock()) {
throw new SomeException();
}
}
}
...etc., but that could get very complex very fast, because the Lock implementations need their own mechanism to unlock themselves.
This might still not be exactly what you're looking for, but hopefully it gives you some ideas.
Related
I have an application that takes json objects from a queue, deserializes them to a model, applies a list of filters, and sends the objects that pass all filters through to another queue.
The two complicating criteria are:
The set of filters is determined and injected via Spring profile at startup.
The type of object that the json is being deserialized to is also determined the by the Spring profile at startup.
The following solution is ugly because it involves casting:
public class MessageTypeOne {
public int someField;
}
public class MessageTypeTwo {
public int otherField;
}
public interface MessageFilter {
boolean doesFilterPass(Object object);
}
#Component
#Profile("ProfileOne")
public class OneOfMyMessageFilters implements MessageFilter {
public boolean doesFilterPass(Object object) {
MessageTypeOne message = (MessageTypeOne)object;
if (message.someField == something) {
return false;
} else return true;
}
}
#Component
#Profile("ProfileTwo")
public class AnotherOneOfMyMessageFilters implements MessageFilter {
public boolean doesFilterPass(Object object) {
MessageTypeTwo message = (MessageTypeTwo)object;
if (message.otherField == something) {
return false;
} else return true;
}
}
#Service
public class MessageFilterService {
// injected at runtime via Spring profile
private Set<MessageFilter> messageFilters
#AutoWired
public MessageFilterService(Set<MessageFilter> messageFilters) {
this.messageFilters = messageFilters;
}
public boolean passesAllFilters(Object object) throws IOException {
for (MessageFilter filter : messageFilters) {
if (!filter.doesFilterPass(object)) {
return false;
}
}
return true;
}
}
What's the cleanest pattern for cases like these? I've read about the visitor pattern but I'm not sure that's any better than casting like this.
As far as design pattern is concerned, I think it is of type Strategy pattern. I am not talking about Spring way of implementation. You may have n number of filters, but you have to choose based upon the context. So strategy pattern is best fitted here. Others can provide other patterns. You can strategy pattern in the below link.
https://en.wikipedia.org/wiki/Strategy_pattern
What about visitor pattern with Java reflection? Here is an old article:
https://www.javaworld.com/article/2077602/java-tip-98--reflect-on-the-visitor-design-pattern.html
When you want to decouple messages from filters and relation is many to many you can always use Chain of Responsibility.
#Service
public class MessageFiltersAggregator {
private MessageFilter chainEntryNode;
#AutoWired
public MessageFilterService(Set<MessageFilter> messageFilters) {
this.chainEntryNode = buildChain(messageFilters);
}
public boolean passesAllFilters(Object object) throws IOException {
return chainEntryNode.doesFilterPass(object);
}
}
You need to implement buildChain method which creates chain from collection. Of course, each element in chain should have next property. In this case MessageFilter could look like below:
public abstract class MessageFilter {
private MessageFilter next;
//constructors, setters, etc
public boolean doesFilterPass(Object object) {
boolean res = true;
if (canHandle(object)) {
res = validate(object);
}
return res && next.doesFilterPass(object);
}
public abstract boolean validate(Object object);
public abstract boolean canHandle(Object object);
}
Abstract class contains chain logic you just need to implement two methods in each subclass. One of implementation could look like below:
public class AnotherOneOfMyMessageFilters extends MessageFilter {
public boolean canHandle(Object object) {
return object instanceof MessageTypeTwo;
}
public boolean validate(Object object) {
MessageTypeTwo message = (MessageTypeTwo)object;
return message.otherField == something;
}
}
All above classes are just example created without IDE so could have issues in syntax but should give you an idea how it should work.
See also:
Chain of Responsibility in Java
Chain of Responsibility Design Pattern in Java
If I understand your problem correctly, then it's possible to configure your Spring profile in a way that makes your filters throw ClassCastExceptions.
Assuming that you configuration options are the way you want, then it demonstrates the only real problem with your design -- your filters can be applied to any Object, and that's what the interface says -- doesFilterPass( Object ) -- but your filters only really work with certain types of objects.
That's what you need to fix. If the filter is applied to a strange type of object, does it pass or fail? You can decide this on a per-filter basis and then just fix it like this:
public boolean doesFilterPass(Object object) {
if (!(object instanceOf MessageTypeTwo)) {
return true;
}
MessageTypeTwo message = (MessageTypeTwo)object;
if (message.otherField == something) {
return false;
} else return true;
}
Easy peasy.
I know you don't like the cast, but it's a direct result of the configuration options you provide -- the profile can be configured to apply filters to any kind of object. You just need to support that, and that means there has to be casting somewhere.
This became much cleaner with generics. Since I know what type of Object each filter can handle I can just do this, eliminating the casting:
public class MessageTypeOne {
public int someField;
}
public class MessageTypeTwo {
public int otherField;
}
public interface MessageFilter<T> {
boolean doesFilterPass(T message);
}
#Component
#Profile("ProfileOne")
public class OneOfMyMessageFilters<T extends MessageTypeOne> implements MessageFilter<T> {
public boolean doesFilterPass(MessageTypeOne message) {
if (message.someField == something) {
return false;
} else return true;
}
}
#Component
#Profile("ProfileTwo")
public class AnotherOneOfMyMessageFilters<T extends MessageTypeTwo> implements MessageFilter<T> {
public boolean doesFilterPass(MessageTypeTwo message) {
if (message.otherField == something) {
return false;
} else return true;
}
}
#Service
public class MessageFilterServiceImpl<T> implements MessageFilterService<T> {
// injected at runtime via Spring profile
private Set<MessageFilter<T>> messageFilters
#AutoWired
public MessageFilterService(Set<MessageFilter<T>> messageFilters) {
this.messageFilters = messageFilters;
}
public boolean passesAllFilters(T message) throws IOException {
for (MessageFilter filter : messageFilters) {
if (!filter.doesFilterPass(message)) {
return false;
}
}
return true;
}
}
public interface MessageFilterService<T> {
boolean passesAllFilters(T rawEvent) throws IllegalArgumentException;
}
I need something that seems not so specific but anyway I was unable to come up with nice and sophisticated solution.
Say I have very simple hibernate/jpa entity:
#Entity(name="entity")
public class Type {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(unique = true, nullable = false)
private String name;
#Column(unique = false, nullable = false)
private boolean defaultType;
}
What i need is to somehow annotate defaultType field so only (and exactly) one persisted entity have this value as true. When new entity get persisted with this defaultType as true, the old one (with defaultType=true) entity has to be altered and its defaultType value changed to false. Also if any entity get changed (its defaultType got changed to true), same rule should apply.
As far I know this can be achieved inside business logic (e.g. in DAO layer), with DB trigger or with hibernates interceptor or event (If there is another way, please let me know). I tried with DAO solution but it's kind of bad solution because it can be bypassed and it is really clumsy for such simple operation. DB triggers can not be added with hibernate/jpa annotations (if I am not mistaken) and i am not sure how to make this functionality with hibernate interceptors/events.
So, what is best solution for this problem?
You need use Callback method in JPA, for example PreUpdate or PostUpdate, for instance:
#Entity
#EntityListeners(com.acme.AlertMonitor.class) // set callback method in another class
public class Account {
Long accountId;
Integer balance;
boolean preferred;
#Id
public Long getAccountId() { ... }
...
public Integer getBalance() { ... }
...
#Transient
public boolean isPreferred() { ... }
...
public void deposit(Integer amount) { ... }
public Integer withdraw(Integer amount) throws NSFException {... }
#PreUpdate // callback method in some class
protected void validateCreate() {
if (getBalance() < MIN_REQUIRED_BALANCE)
throw new AccountException("Insufficient balance to open an
account");
}
#PostUpdate // callback method in some class
protected void adjustPreferredStatus() {
preferred =
(getBalance() >= AccountManager.getPreferredStatusLevel());
}
}
// callback method in another class
public class AlertMonitor {
#PreUpdate // callback method in another class
public void updateAccountAlert(Account acct) {
Alerts.sendMarketingInfo(acct.getAccountId(), acct.getBalance());
}
}
Update: About your question, If I undestand what you want, this code may help you:
#Entity(name="entity")
#EntityListeners(com.yourpackage.TypeListner.class)
public class Type {
...
#Column(unique = false, nullable = false)
private boolean defaultType;
}
public class TypeListner {
pivate static Type objectWithTrue = null;
public void init() { // call this method when application is started
List<Type> results = entityManager
.createQuery("from Type", Type.class)
.getResultList();
for(Type type: results) {
if(type.getDefaultType()) {
objectWithTrue = type;
}
}
}
private void changeDefaultType(Type changed) {
if(changed.getDefaultType()) {
if(changed != objectWithTrue && objectWithTrue != null) {
objectWithTrue.setDefaultType(false);
}
objectWithTrue = changed;
}
}
#PostPresist
public void newType(Type changed) {
changeDefaultType(changed);
}
#PostUpdate
public void updateType(Type changed) {
changeDefaultType(changed);
}
#PreRemove
public void removeType(Type changed) {
if(changed.getDefaultType() && objectWithTrue == changed) {
objectWithTrue = null;
}
}
OR
You can use listner #PreUpdate and #PrePresist and every times overwrite all Type objects without store any variable (it isn't so good for perfomance then first example, but more reliable):
#PreUpdate
void updateType(Type changed) {
if(changed.getDefaultType()
List<Type> results = entityManager
.createQuery("from Type", Type.class)
.getResultList();
for(Type type: results) {
if(changed != type && type.getDefaultType()) {
type.setDefaultType(false);
}
}
}
}
Could you guys please help me find where I made a mistake ?
I switched from SimpleBeanEditorDriver to RequestFactoryEditorDriver and my code no longer saves full graph even though with() method is called. But it correctly loads full graph in the constructor.
Could it be caused by circular reference between OrganizationProxy and PersonProxy ? I don't know what else to think :( It worked with SimpleBeanEditorDriver though.
Below is my client code. Let me know if you want me to add sources of proxies to this question (or you can see them here).
public class NewOrderView extends Composite
{
interface Binder extends UiBinder<Widget, NewOrderView> {}
private static Binder uiBinder = GWT.create(Binder.class);
interface Driver extends RequestFactoryEditorDriver<OrganizationProxy, OrganizationEditor> {}
Driver driver = GWT.create(Driver.class);
#UiField
Button save;
#UiField
OrganizationEditor orgEditor;
AdminRequestFactory requestFactory;
AdminRequestFactory.OrderRequestContext requestContext;
OrganizationProxy organization;
public NewOrderView()
{
initWidget(uiBinder.createAndBindUi(this));
requestFactory = createFactory();
requestContext = requestFactory.contextOrder();
driver.initialize(requestFactory, orgEditor);
String[] paths = driver.getPaths();
createFactory().contextOrder().findOrganizationById(1).with(paths).fire(new Receiver<OrganizationProxy>()
{
#Override
public void onSuccess(OrganizationProxy response)
{
if (response == null)
{
organization = requestContext.create(OrganizationProxy.class);
organization.setContactPerson(requestContext.create(PersonProxy.class));
} else
organization = requestContext.edit(response);
driver.edit(organization, requestContext);
}
#Override
public void onFailure(ServerFailure error)
{
createConfirmationDialogBox(error.getMessage()).center();
}
});
}
private static AdminRequestFactory createFactory()
{
AdminRequestFactory factory = GWT.create(AdminRequestFactory.class);
factory.initialize(new SimpleEventBus());
return factory;
}
#UiHandler("save")
void buttonClick(ClickEvent e)
{
e.stopPropagation();
save.setEnabled(false);
try
{
AdminRequestFactory.OrderRequestContext ctx = (AdminRequestFactory.OrderRequestContext) driver.flush();
if (!driver.hasErrors())
{
// Link to each other
PersonProxy contactPerson = organization.getContactPerson();
contactPerson.setOrganization(organization);
String[] paths = driver.getPaths();
ctx.saveOrganization(organization).with(paths).fire(new Receiver<Void>()
{
#Override
public void onSuccess(Void arg0)
{
createConfirmationDialogBox("Saved!").center();
}
#Override
public void onFailure(ServerFailure error)
{
createConfirmationDialogBox(error.getMessage()).center();
}
});
}
} finally
{
save.setEnabled(true);
}
}
}
with() is only used for retrieval of information, so your with() use with a void return type is useless (but harmless).
Whether a full graph is persisted is entirely up to your server-side code, which is intimately bound to your persistence API (JPA, JDO, etc.)
First, check that the Organization object you receive in your save() method on the server-side is correctly populated. If it's not the case, check your Locators (and/or static findXxx methods) ; otherwise, check your save() method's code.
Judging from the code above, I can't see a reason why it wouldn't work.
It took me some time to realize that the problem was the composite id of Person entity.
Below is the code snippet of PojoLocator that is used by my proxy entities.
public class PojoLocator extends Locator<DatastoreObject, Long>
{
#Override
public DatastoreObject find(Class<? extends DatastoreObject> clazz, Long id)
{
}
#Override
public Long getId(DatastoreObject domainObject)
{
}
}
In order to fetch child entity from DataStore you need to have id of a parent class. In order to achieve that I switched "ID class" for Locator<> to String which represents textual form of Objectify's Key<> class.
Here is how to looks now:
public class PojoLocator extends Locator<DatastoreObject, String>
{
#Override
public DatastoreObject find(Class<? extends DatastoreObject> clazz, String id)
{
Key<DatastoreObject> key = Key.create(id);
return ofy.load(key);
}
#Override
public String getId(DatastoreObject domainObject)
{
if (domainObject.getId() != null)
{
Key<DatastoreObject> key = ofy.fact().getKey(domainObject);
return key.getString();
} else
return null;
}
}
Please note that your implementation may slightly differ because I'm using Objectify4.
I have UI automation tests. Tests involve three entities -
Data object class - data to be filled in forms. Herein each form on a page could be represented by a different data object.
Helper class - which fills in data in a form on page
Test class - which uses data object and helper class to perform test.
Following is the cut down version of test -
public class ParallelDataObject {
HelperClass helperClass = new HelperClass();
Data data;
#BeforeMethod
public void setTestData() {
data = new Data();
helperClass.setData(data);
}
#Test
public void passM1() {
helperClass.verifyFlag();
}
#Test
public void failM2() {
data.setFlag(false);
helperClass.setData(data);
helperClass.verifyFlag();
}
#Test
public void passM3() {
helperClass.verifyFlag();
}
#Test
public void failM4() {
data.setFlag(false);
helperClass.setData(data);
helperClass.verifyFlag();
}
}
class HelperClass {
Data data;
public void setData(Data data) {
synchronized (data) {
this.data = data;
}
}
public void verifyFlag() {
synchronized (data) {
assert data.getFlag();
}
}
}
class Data {
private boolean flag;
public Data() {
flag = true;
}
public Data setFlag(boolean flag) {
synchronized (this) {
this.flag = flag;
return this;
}
}
public boolean getFlag() {
synchronized (this) {
return flag;
}
}
When executing methods in parallel I encountered weird results as data is not thread safe. Then I incorporated synchronize blocks but yet I encounter weird results.
I am sure I have messed up how synchronization should be used here in. Any insight?
I did one more exercise. I set up another Test class exactly same as first test class. I removed all synchronization from helper and data class. When I run classes in parallel (instead of methods). Test results are as expected. Why don't I run in to concurrency when I execute classes in parallel, even though they user same helper class and data object?
HelperClass and Data are thread-safe.
The problem is that some of your test methods perform several operations. And sequence of the operations in test method is not atomic as long as it not synchronized.
For example during failM4 execution the state of helperClass might be modified by other thread.
I'd recommend you to not use shared state between test methods because synchronization will nullify the advantages of concurrent tests execution.
Consider using ThreadLocal. This way each thread has its own copy of HelperClass. Note that synchronizing separate methods won't give you anything - changes made in one test (in one thread) are visible by other tests
class ParallelDataObject {
private final ThreadLocal<HelperClass> helperClassThreadLocal = new ThreadLocal<HelperClass>() {
#Override
protected HelperClass initialValue() {
return new HelperClass(new Data());
}
};
private HelperClass helperClass() {
return helperClassThreadLocal.get();
}
#Test
public void passM1() {
helperClass().verifyFlag();
}
#Test
public void failM2() {
helperClass().getData().setFlag(false);
helperClass().verifyFlag();
}
}
class HelperClass {
private final Data data;
public HelperClass(Data data) {
this.data = data;
}
public Data getData() {
return data;
}
public void verifyFlag() {
assert data.getFlag();
}
}
class Data {
private boolean flag = true;
public Data setFlag(boolean flag) {
this.flag = flag;
return this;
}
public boolean getFlag() {
return flag;
}
}
Other improvements:
passM3 and failM4 were superfluous
since HelperClass requires an instance of Data to work, it should declare it using constructor dependency
when using:
synchronized(this)
wrapping whole method body, consider using synchronized keyword in method declaration instead (more readable).
synchronization is no longer needed with ThreadLocals
Test statelessness
#gpeche makes a good suggestion that tests should be independent. Unfortunately (why, oh why!?) JUnit reuses the same test case class instance (ParallelDataObject in this case) for all test methods execution. This means that assigning any stateful objects to test case class fields is dangerous and must be avoided.
In this particular case the OP would have to create a new instance of HelperClass in each test method (which, in fact, isn't such a bad idea):
class ParallelDataObject {
#Test
public void passM1() {
final HelperClass helperClass = new HelperClass(new Data());
helperClass.verifyFlag();
}
#Test
public void failM2() {
final Data data = new Data();
data.setFlag(false);
final HelperClass helperClass = new HelperClass(data);
helperClass.verifyFlag();
}
}
Not sure how to describe this for sure, but I think I've boiled down what I want to do in the title. To elaborate, I'm looking for a design pattern that would let me have a implementation of a service that would in one situation return the result of a call synchronously but in another case return details on how to complete the call asynchronously (say a job ID).
Maybe just by defining the problem like that it's clear that what I'm trying to do breaks the idea of designing an interface contract. Could be headed in the wrong direction entirely.
What I was thinking of was possibly something like this:
public class Data {
private int id;
/* getter/setter */
}
public class QueuedData extends Data {
private int jobId;
/* getter/setter */
}
public interface MyService {
public Data fetchData(int id);
}
public class SyncedMyService implements MyService {
private SyncDao syncDao;
public Data fetchData(int id) {
return syncDao.getData(id);
}
}
public class QueuedMyService implements MyService {
private JobQueue queue;
public QueuedData fetchData(int id) {
int jobId = queue.startGetData(id);
QueuedData queuedData = createQueuedDate(jobId);
return queuedData;
}
}
Is this a sensible way to go about this task? Thanks for any advice. (there's probably a design-pattern book I should be reading)
This is very similar to the Future pattern used in the java.util.concurrent package. A Future represents a result that will be available in the future after the computation is completed in a separate thread. If the computation is already complete before the result is required, the computed value is returned. Else the call to get the result blocks till the computation is over.
So I think this pattern is the right way to go about having both synchronous and asynchronous services.
This is how you can implement the solution using Future:
public class Data {
private int id;
private final String name;
Data(String name) { this.name = name; }
public String getName() { return name; }
}
public class FutureData extends Data {
private int id;
private final Future<String> nameFuture;
FutureData(Future<String> nameFuture) { this.nameFuture = nameFuture; }
#Override public String getName() { return nameFuture.get(); }
}
public interface MyService {
public Data fetchData(int id);
}
public class SyncMyService implements MyService {
private SyncDao syncDao;
public Data fetchData(int id) {
return syncDao.getData(id);
}
}
public class AsyncMyService implements MyService {
private static final ExecutorService executor =
Executors.newFixedThreadPool(10);
public FutureData fetchData(final int id) {
Future<String> future = executor.submit(new Callable<String>() {
public String call() {
String name;
//some long computation that computes the name using the id given
return name;
}
});
FutureData futureData = new FutureData(future);
return futureData;
}
}
For Quartz just replace the ExecutorService with the JobQueue and use Quartz's equivalent of Future.
This is a fine use of Inheritance. Your SynchedMyService and QueuedMyService are following the contract/rules designated by MyService.
Also by having the fetchData() method return a type Data, you are allowing yourself the ability to build on top of the Data object and return more complex objects (like QueuedData)
If you don't want to have the logic of which of the classes to instantiate each time. Look at the Factory design pattern to assist you as you continue to grow your application