Когато пиша някакъв код се стремя той да е макасимално четим(разбирай опростен и подреден), но с времето нещата набъбват и четимоста отстъпва място на елегантноста. Ето и конкретния случай. Имам си класа SFAdapter, който прави връзката към SalesForce.com и изпънява заявките. Този менаджер може да бъде всъшност всякакъв примерно към базата. В него имах само три неща - инициализация на връзката, логин и метод за подаване на заявките:
................ public SFAdapter(final String sfUser, final String sfPass) { if( sfUser==null || sfPass==null ) return; this.sfUser=sfUser; this.sfPass=sfPass; try { binding = (SoapBindingStub) new SforceServiceLocator().getSoap(new URL("some_url")); } catch (Exception e) { ..................... protected void doLogin() ............. public QueryResult query(final String ) .............
Да обаче проеката си нараства и се налага да се добавят все повече и повече методи, който са абсолуютно еднакви като структура:
public типа_които_ще_върне име(параметри) { try { изпънява_заявката // следват нещата, които ще направи при грешка } catch (UnexpectedErrorFault uef) {
И какво се получава - много писане. Верно проситчки неща ама много писане беее. Сегиз-тогиз е добре да се консултираш. Ето в какво се превърна кода след консултацията с колегите.
Повтарящите се неща да се на едно място. Операциите трябва да се уеднаквят. Това е посоката на мислене. И самия код:
public void delete(final String[] ids) { executeInSf(new WriteCommand<Object>() { public Object execute() throws UnexpectedErrorFault, RemoteException { binding.delete(ids); return null; } }); } public SObject[] retrieve(final String fieldList, final String sObjectType, final String[] ids) { return executeInSf(new ReadCommand<SObject[]>() { public SObject[] execute() throws UnexpectedErrorFault, RemoteException { return binding.retrieve(fieldList, sObjectType, ids); } }); } private <T> T executeInSf(Command<T> c) throws RecoverableSFException, CriticalSFException { try { return c.execute(); } catch (UnexpectedErrorFault uef) { if (uef.getExceptionCode().equals(ExceptionCode.INVALID_SESSION_ID)) { try { doLogin(); return c.execute(); } catch (UnexpectedErrorFault e) { throw c.createException(e); } catch (InvalidIdFault e) { ................... } interface Command<T extends Object> { T execute() throws UnexpectedErrorFault, RemoteException; RecoverableSFException createException(Exception cause); } abstract class ReadCommand<T extends Object> implements Command<T>{ public SFReadException createException(Exception cause) { return new SFReadException(cause); } } abstract class WriteCommand<T extends Object> implements Command<T>{ public SFWriteException createException(Exception cause) { return new SFWriteException(cause); } }
С простички думи. Операцията, която трябва да се изпълни я дефинираме в интерфейс(Command), а нещата които се повтарят в един метод, който приема като параметър този инерфейс(executeInSf). Следва и самото изпълнение(retrieve), която не прави нищо друго освен да иплементира изпълнението на операцията - execute метода.
NOTE: В метода delete забележете, че нама return, а execute трябва задължително да върне стойност и тя е null.
Малко се усложниха нещата, защото имам два вида грешки:
try { doLogin(); return c.execute(); } catch (UnexpectedErrorFault e) { // see here! throw c.createException(e); ...........
Правилото е, ако има има различия и допълнителни неща те се отделят в абстрактни класове, които наследяват инерфейса.
И всичко това само, защото в Java не можем да предаваме функции. Ако беше така, на executeInSf щахме да предадем функцията, която трябва да се изпълни. Може би посоката на мислене трябва да е към езици за функционално програмиране.
2 comments:
Добре де, толкова е просто. Разбере ли човек как да присвои анонимна функция конците се разплитат.
В Java се използва callback:
interface IFromIAndI {
Integer call(Integer a, Integer b);
}
IFromIAndI add_two_integers = new IFromIAndI() {
public Integer call(final Integer a, final Integer b) {
return a + b;
}
};
Това е равносилно на:
add_two_integers = lambda...
След като сме ги дефинирали по този начин нещата използването е много лесно:
add_two_integers.call(35, 42);
т.е. извикваме "анонимната" функция.
Ама все от много далече почваш нещата или просто обичаш да пишеш много :)
Здрасти,
Това е тъй наречения "Execute around" идиом. Виж слединя код:
public interface InputStreamAction
{
void useStream(InputStream stream) throws IOException;
}
// Somewhere else
public void executeWithFile(String filename, InputStreamAction action)
throws IOException
{
InputStream stream = new FileInputStream(filename);
try {
action.useStream(stream);
} finally {
stream.close();
}
}
// Calling it
executeWithFile("filename.txt", new InputStreamAction()
{
public void useStream(InputStream stream) throws IOException
{
// Code to use the stream goes here
// The calling code doesn't need to worry about the open/clean-up side.
// It will be taken care of by "executeWithFile"
}
});
Хвърли и един поглед на Template Method Pattern ;)
Post a Comment