По-интелигентно развитие на Java

Бърза и проста схема за ускоряване на развитието на широкомащабни Java приложения включва използването на интерфейси. Java интерфейсите са план за функционалността, съдържаща се в асоцииран обект.

Включвайки интерфейси в следващия си проект, ще забележите предимства през целия жизнен цикъл на усилията ви за развитие. Техниката на кодиране на интерфейси, а не на обекти, ще подобри ефективността на екипа за разработки чрез:

  • Позволява на екипа за разработка бързо да установи взаимодействията между необходимите обекти, без да налага принудително ранно дефиниране на поддържащите обекти
  • Даване възможност на разработчиците да се концентрират върху своите задачи за разработка със знанието, че интеграцията вече е взета под внимание
  • Осигуряване на гъвкавост, така че нови реализации на интерфейсите да могат да се добавят към съществуващата система без значителна модификация на кода
  • Прилагане на договорите, договорени от членовете на екипа за разработка, за да се гарантира, че всички обекти си взаимодействат според проектирането

Преглед

Тъй като обектно-ориентираните усилия за развитие включват взаимодействието на обектите, от съществено значение е да се разработят и наложат силни договори между тези обекти. Техниката на кодиране на интерфейси включва използването на интерфейси, а не на обекти, като основен метод за комуникация.

Тази статия ще запознае потребителя с концепцията за кодиране на интерфейси чрез прост пример. Ще последва подробен пример, който помага да се демонстрира стойността на тази схема в по-голяма система, изискваща множество разработчици. Преди да стигнем до примерния код обаче, нека разгледаме предимствата на кодирането на интерфейсите.

Защо да кодирам към интерфейси?

Интерфейсът на Java е договор за разработка. Той гарантира, че определен обект удовлетворява даден набор от методи. Интерфейсите се използват в Java API за определяне на необходимата функционалност за взаимодействие с обекти. Примери за използване на интерфейс са механизмите за обратно извикване ( Event Listeners), шаблони ( Observer) и спецификации ( Runnable, Serializable).

Кодирането в интерфейси е техника, чрез която разработчиците могат да излагат определени методи на обект на други обекти в системата. Разработчиците, които получават внедрения на тези интерфейси, имат възможността да кодират интерфейса вместо кодиране на самия обект. С други думи, разработчиците ще напишат код, който не взаимодейства директно с обект като такъв, а по-скоро с изпълнението на интерфейса на този обект.

Друга причина за кодиране на интерфейси, а не на обекти, е, че тя осигурява по-висока ефективност в различните фази на жизнения цикъл на системата:

  • Проектиране : методите на обект могат бързо да бъдат определени и публикувани за всички засегнати разработчици
  • Разработка : Java компилаторът гарантира, че всички методи на интерфейса са изпълнени с правилния подпис и че всички промени в интерфейса са незабавно видими за други разработчици
  • Интеграция : има възможност за бързо свързване на класове или подсистеми заедно, поради техните добре установени интерфейси
  • Тестване : интерфейсите помагат да се изолират грешки, защото те ограничават обхвата на възможна логическа грешка до дадена подгрупа от методи

Има някои режийни разходи, свързани с тази техника за разработка, поради необходимата кодова инфраструктура. Тази инфраструктура включва както интерфейси за взаимодействия между обекти, така и код за извикване за създаване на реализации на интерфейси. Тези режийни разходи са незначителни в сравнение с лекотата и ползата от използването на интерфейси, както е описано.

Основен пример

За да обясня допълнително концепцията за кодиране на интерфейси, създадох прост пример. Въпреки че този пример е очевидно тривиален, той демонстрира някои от предимствата, споменати по-горе.

Помислете за простия пример за клас, Carкойто реализира интерфейс Vehicle. Интерфейсът Vehicleима един метод, наречен start(). Class Carще приложи интерфейса, като предостави start()метод. Друга функционалност в Carкласа е изключена за по-голяма яснота.

интерфейс Vehicle {// Всички внедрения на превозни средства трябва да прилагат метода за стартиране public void start (); } клас Автомобилни приспособления Превозно средство {// Изисква се за внедряване на превозното средство public void start () {...}}

След като положихме основите на Carобекта, можем да създадем друг обект, наречен Valet. Това е най Valet-те години работа, за да стартират Carи да го приведе към покровителя на ресторант. В Valetобекта може да се запише без интерфейси, както следва:

class Valet {обществен автомобил getCar (Car c) {...}} 

В Valetобекта има метод, наречен getCarкоято връща Carобект. Този пример за код отговаря на функционалните изисквания на системата, но завинаги свързва Valetобекта с този на Car. В тази ситуация се казва, че двата обекта са плътно свързани. В Valetобекта се изисква познаване на Carобекта и има достъп до всички публични методи и променливи, които се съдържат в рамките на този обект. Най-добре е да избягвате толкова тясно свързване на код, защото това увеличава зависимостите и намалява гъвкавостта.

За кодиране на Valetобекта с помощта на интерфейси може да се използва следното изпълнение:

class Valet {обществено превозно средство getVehicle (превозно средство c) {...}} 

Въпреки че промените в кода са доста незначителни - промяната на препратките от Carкъм Vehicle- ефектите върху цикъла на разработка са значителни. Използвайки второто внедряване, Valetима познания само за методите и променливите, дефинирани в Vehicleинтерфейса. Всички други публични методи и данни, съдържащи се в конкретното изпълнение на Vehicleинтерфейса, са скрити от потребителя на Vehicleобекта.

Тази проста промяна на кода е осигурила правилното прикриване на информация и изпълнение от други обекти и следователно елиминира възможността разработчиците да използват нежелани методи.

Създаване на интерфейсния обект

Последният въпрос, който трябва да се обсъди по отношение на тази техника за разработка, е създаването на интерфейсни обекти. Въпреки че е възможно да се създаде нов екземпляр на клас с помощта на newоператора, не е възможно директно да се създаде екземпляр на интерфейс. За да създадете реализация на интерфейс, трябва да създадете екземпляр на обекта и да го прехвърлите към желания интерфейс. Следователно разработчикът, който притежава обектния код, може да бъде отговорен както за създаването на екземпляра на обекта, така и за извършването на кастинга.

Този процес на създаване може да бъде постигнат с помощта на Factoryшаблон, при който външен обект извиква статичен createXYZ()метод на a Factoryи връща интерфейс. Може да се постигне и ако разработчик извика метод за друг обект и му предаде интерфейс вместо действителния клас. Това би било аналогично на предаването на Enumerationинтерфейс вместо Vectorили Hashtable.

Подробен пример

За да демонстрирам използването на тази схема за по-голям проект, създадох примера за планиране на срещи. Този график има три основни компонента: ресурсите (конферентна зала и участник в събранието), събитието (самото събрание) и планировчикът (този, който поддържа календара на ресурсите).

Let's assume these three components were to be developed by three different developers. The goal of each developer should be to establish the usage of his or her component and publish it to the other developers on the project.

Consider the example of a Person. A Person may implement numerous methods but will implement the Resource interface for this application. I have created the Resource interface with all the necessary accessor methods for all resources used in this example (shown below):

public interface Resource { public String getID(); public String getName(); public void addOccurrence( Occurrence o); } 

At this point, the developer of the Person functionality has published the interface by which all users can access the information stored in the Person object. Coding to the interface helps ensure that no developers are using the Person object in an incorrect manner. The developer of the Scheduler object can now use the methods contained in the Resource interface to access the information and functionality necessary to create and maintain the schedule of the Person object.

The Occurrence interface contains methods necessary for the scheduling of an Occurrence. This can be a conference, travel plan, or any other scheduling event. The Occurrence interface is shown below:

public interface Occurrence { public void setEndDatetime(Date d); public Date getEndDatetime(); public void setStartDatetime(Date d); public Date getStartDatetime(); public void setDescription(String description); public String getDescription(); public void addResource(Resource r); public Resource[] getResources(); public boolean occursOn( Date d); } 

The Scheduler code uses the Resource interface and the Occurrence interface to maintain the schedule of a resource. Notice that the Scheduler does not have any knowledge of the entity for which it is maintaining the schedule:

public class Scheduler implements Schedule{ Vector schedule = null; public Scheduler(){ schedule = new Vector(); } public void addOccurrence(Occurrence o){ schedule.addElement(o); } public void removeOccurrence(Occurrence o){ schedule.removeElement(o); } public Occurrence getOccurrence(Date d) { Enumeration scheduleElements = schedule.elements(); Occurrence o = null; while ( scheduleElements.hasMoreElements() ) { o = (Occurrence) scheduleElements.nextElement(); // For this simple example, the occurrence matches if // the datetime isthe meeting start time. This logic // can be made more complex as required. if ( o.getStartDatetime() == d) { break; } } return o; } } 

This example shows the power of interfaces in the development phases of a system. Each of the subsystems has knowledge only of the interface through which it must communicate -- no knowledge of the implementation is required. If each of the building blocks in the above example were to be further developed by teams of developers, their efforts would be simplified due to the enforcement of these interface contracts.

Final thoughts on interfaces

This article has demonstrated some of the benefits of coding to interfaces. This technique enables greater efficiency throughout each phase of the development lifecycle.

During the design phases of the project, interfaces allow the quick establishment of the desired interactions among objects. The implementation objects associated with a given interface can be defined after the methods and requirements for that interface are specified. The more quickly the interaction is established, the more quickly the design phase can progress into development.

Interfaces give developers the ability to expose and limit certain methods and information to the users of their objects without changing the permissions and internal structure of the object itself. The use of interfaces can help eliminate the pesky bugs that appear when code developed by multiple development teams is integrated.

Contract enforcement is provided by the interface. Because the interface is generally agreed upon during the design phase of the project, the developers have the ability to concentrate on their individual modules without having to worry about the modules of their colleagues. Integrating these subsystems is made more efficient by the fact that the contracts have already been enforced throughout the development phase.

For testing purposes, a simple driver object can be created to implement the agreed-upon interfaces. Using this object, developers can continue their work with the knowledge that they are using the proper methods to access the object. When the objects are deployed in a test environment, the driver classes are replaced by the true classes, allowing the object to be tested without code or property changes.

This scheme provides the capability for easy expansion of this system; in our example, we could expand the code to include more forms of resources, such as meeting rooms and audio/video equipment. Any additional implementation of the Resource interface will fit into the established mechanism without modifying the existing code. Large-scale projects using this scheme could be designed and implemented in such a way that additional functionality can be added without major modification to the infrastructure. As an example, the ConferenceRoom object was created. This object implements the Resource interface and can interact with the Schedule and Occurrence implementers without changing the infrastructure.

Друго предимство е централизираното местоположение на кода. Ако трябва да се добавят нови методи към Resourceинтерфейса, всички реализации на този интерфейс ще бъдат идентифицирани като изискващи промяна. Това ще намали разследването, необходимо за определяне на възможното въздействие на промените в интерфейса.

В допълнение към предимствата за развитието, техниката, представена в тази статия, осигурява на управлението на проекта увереността, че моделите на междуобектни или междусистемни комуникации са установени и наложени през целия цикъл на развитие. Това намалява риска от откази по време на фазите на интегриране и тестване на проекта.