Keywords

These keywords were added by machine and not by the authors. This process is experimental and the keywords may be updated as the learning algorithm improves.

1 Introduction

In the mid-nineties, the Gang of Four’s work on software design patterns [1] paved the way for important advances in software quality; presently, many valuable experienced designers’ “best practices” are not only published but effectively used by the software development community. From very basic, abstract, patterns that can be used as building blocks of several more complex ones, to business-specific patterns and frameworks, dozens of design patterns have been proposed, e.g. [28], establishing a kind of common language between development teams, which substantially enriches their communication, and hence the whole design process.

Despite their widespread usage in the object-oriented paradigm, on which a lot of the work has been focused, effort has also been made in adapting these best practices to other paradigms – service-oriented [3], functional [911], logic [12] and others – and in finding new paradigm-specific patterns. As several of these authors observed, studying design patterns in different programming paradigms is far from being a trivial task: each paradigm has its specific features, meaning that patterns that are very straightforward in one paradigm can be very complex in another, and vice-versa.

In this spirit, we carried the task of identifying several basic and other, more complex, patterns in the paradigm of Mdl-programs [13] – which join description logics with rules (expressed as a Datalog-like logic program) –, a powerful and expressive approach to reasoning over general knowledge bases or ontologies that generalizes the original dl-programs [14]. The goal of this paper is to extend the original presentation in [15] with a more detailed analysis of the limitations that arise in this framework.

This work should be seen as quite distinct from that on ontology design patterns [16]. In the setting of Mdl-programs, ontologies are seen as immutable, being used and not changed, under the coordination of a set of rules. Our patterns focus therefore almost exclusively on these rules; thus, ontology design patterns and design patterns for Mdl-programs should in general be seen as two complementary techniques, and not as alternatives.

1.1 Motivation

The usefulness of combining description logics with rule-based reasoning systems led to the introduction of dl-programs [14, 17], which couple a description logic knowledge base with a generalized logic program, interacting by means of special atoms, the dl-atoms. These programs were later generalized to include several knowledge bases, yielding Mdl-programs [13].

Looking at Mdl-programs, it is clear that they represent a completely different programming paradigm – not only are they closely related to the logic programming paradigm, but they involve description logic knowledge bases, in the presence of which the study of design patterns attains a different quality: on the one hand, some patterns become trivial (such as Façade) or meaningless (such as Dynamic Binding or Singleton), on the other hand some patterns pose totally new problems that have not been addressed in other paradigms where they do not arise (such as Proxy, which we will discuss in Sect. 5).

Mdl-programs, combining description logic knowledge bases and a logic-based rule language, provide the adequate setting for the study of design patterns for the Semantic Web. Indeed, description logics are at the core of the Semantic Web, with a huge effort being currently invested in the interchange between OWL – an extension of the description logic SROIQ and a W3C recommendation – and a diversity of rule languages [18]. The components of an Mdl-program are kept independent, giving them nice modularity properties; furthermore, Mdl-programs keep ontologies separate, which is much more convenient than e.g. merging them: not only is it simpler to have independent knowledge bases (which might even be physically separated, or independently managed), but merging ontologies is in itself a mighty task with its own specific problems [19, 20].

On the other hand, Mdl-programs limit heterogeneity to two different frameworks: description logics for the knowledge bases part and logic programming for the rule part; the latter somehow represents the “conductor” that “coordinates” the other parts. However, they fully support non-monotonicity (even at the level of the description logic knowledge bases as will be seen later by application of a specific basic pattern). Mdl-programs are therefore a simpler framework than other, more powerful, alternatives (such as Hex-programs [21] or multi-context systems [22]), but expressive enough for their use within the Semantic Web.

The remainder of the paper is structured as follows. Section 2 explains Mdl-programs in detail. Section 3 presents seven different design patterns, and Sect. 4 illustrates their combined use by means of a larger example. Section 5 explores limitations and future directions of research, and Sect. 6 summarizes the contributions presented earlier.

2 Mdl-Programs

Multi-description logic programs, the framework in which we will introduce our design patterns, generalize the original definition of dl-programs in [14] to accommodate for several description logic knowledge bases. This construction, introduced in [15] and detailed in [13], is in line with [23], although it sticks to the original operators \(\uplus \) and \(\mathop {\cup \!\!\!^{{}_{\mathbf{-}}}\;}\) in dl-atoms.

A dl-atom relative to a set of knowledge bases \(\{\mathcal L_1,\ldots ,\mathcal L_n\}\) Footnote 1 is

$$\begin{aligned} DL_i\left[ S_1\, op _{1}\, p_1,\ldots ,S_m\, op _{m}\, p_m;Q\right] (\overline{t})\,, \end{aligned}$$

often abbreviated to \(DL_i[\chi ;Q](\overline{t})\), where: (1) \(1\le i\le n\); (2) each \(S_k\), with \(1\le k\le m\), is either a concept or a role from \(\mathcal L_i\) or a special symbol in \(\{=,\ne \}\); (3) \(\, op _{k}\,\in \{\uplus ,\mathop {\cup \!\!\!^{{}_{\mathbf{-}}}\;}\}\); (4) \(p_k\) are the input predicate symbols, which are unary or binary predicate symbols depending on the corresponding \(S_k\) being a concept or a role; and (5) \(Q(\overline{t})\) is a dl-query in the language of \(\mathcal L_i\), that is, it is either a concept inclusion axiom \(F\) or its negation \(\lnot F\), or of the form \(C(t_1)\), \(\lnot C(t_1)\), \(R(t_1,t_2)\), \(\lnot R(t_1,t_2)\), \(=(t_1,t_2)\), \(\not = (t_1,t_2)\), where \(C\) is a concept, \(R\) is a role, \(t\), \(t_1\) and \(t_2\) are terms (variables or constants).

The operators \(\uplus \) and \(\mathop {\cup \!\!\!^{{}_{\mathbf{-}}}\;}\) are used to extend the knowledge base \(\mathcal L_i\) locally, with \(S_k\uplus p_k\) (resp., \(S_k\mathop {\cup \!\!\!^{{}_{\mathbf{-}}}\;}p_k\)) increasing \(S_k\) (resp., \(\lnot S_k\)) by the extension of \(p_k\). Intuitively, the dl-atom above adds this information to \(\mathcal L_i\) and then asks this knowledge base for the set of terms satisfying \(Q(t)\).Footnote 2

A Multi Description Logic program (Mdl-program) is a pair \(\langle \{\mathcal L_1,\ldots ,\mathcal L_n\},\mathcal P\rangle \) where: (1) each \(\mathcal L_i\) is a description logic knowledge base; (2) \(\mathcal P\) is a set of (normal) Mdl-rules, i.e. rules of the form \(a\leftarrow b_1, \ldots , b_k, not \ b_{k+1},\ldots , not \ b_p\) where \(a\) is a logic program atom and each \(b_j\), for \(1\le j\le p\), is either a logic program atom or a dl-atom relative to \(\{\mathcal L_1,\ldots ,\mathcal L_n\}\). Note that \(\mathcal P\) is a generalized logic program, so negation is the usual, closed-world, negation-as-failure. This is in contrast with the \(\mathcal L_i\), which (being description logic knowledge bases) come with an open-world semantics.

The semantics of Mdl-programs [13] is a straightforward generalization of the semantics of dl-programs [14] and will not be discussed here, since it will not be needed explicitly.

A common feature of multi-component systems is the need for entities in one component to “observe” entities in another component. In the setting of Mdl-programs, this is achieved by means of observers. An Mdl-program with observers is \(\langle \{\mathcal L_1,\ldots ,\mathcal L_n\}, \mathcal P,\{\varLambda _1,\ldots ,\varLambda _n\},\{\varPsi _1,\ldots ,\varPsi _n\}\rangle \) where: (1) \(\langle \{\mathcal L_1,\ldots ,\mathcal L_n\},\mathcal P\rangle \) is an Mdl-program; (2) for \(1\le i\le n\), \(\varLambda _i\) is a finite set of pairs \(\langle S,p\rangle \) where \(S\) is a concept, a role, or a negation of either, from \(\mathcal L_i\) and \(p\) is a predicate from \(\mathcal P\); (3) for \(1\le i\le n\), \(\varPsi _i\) is a finite set of pairs \(\langle p,S\rangle \) where \(p\) is a predicate from \(\mathcal P\) and \(S\) is a concept, a role, or a negation of either, from \(\mathcal L_i\). For each pair in \(\varPsi _i\) or \(\varLambda _i\), the arities of \(S\) and \(p\) must coincide. The sets \(\varLambda _1,\ldots ,\varLambda _n,\varPsi _1,\ldots ,\varPsi _n\) will occasionally be referred to as the observers of \(\langle \{\mathcal L_1,\ldots ,\mathcal L_n\},\mathcal P\rangle \). Intuitively, \(\varLambda _i\) contains concepts and roles in \(\mathcal L_i\) that \(\mathcal P\) needs to observe, in the sense that \(\mathcal P\) should be able to detect whenever new facts about them are derived, whereas \(\varPsi _i\) contains the predicates in \(\mathcal P\) that \(\mathcal L_i\) wants to observe. For simplicity, when we consider Mdl-programs with observers that only have one knowledge base, we will omit the braces and refer to them as dl-programs with observers.

Instead of defining formal semantics for Mdl-programs with observers, we introduced a translation of these into (standard) Mdl-programs that reduces observers to syntactic sugar. The above Mdl-program with observers thus implicitly defines the Mdl-program \(\left\langle \{\mathcal L_1,\ldots ,\mathcal L_n\},\mathcal P_{\varLambda _1,\ldots ,\varLambda _n}^{\varPsi _1,\ldots ,\varPsi _n}\right\rangle \) where \(\mathcal P_{\varLambda _1,\ldots ,\varLambda _n}^{\varPsi _1,\ldots ,\varPsi _n}\) is obtained from \(\mathcal P\) by: (1) adding rule \(p(X)\leftarrow DL_i[;S](X)\) for each \(\langle S,p\rangle \in \varLambda _i\), if \(S\) is a concept (and its binary counterpart, if \(S\) is a role); and (2) in each dl-atom \(DL_i[\chi ;Q](t)\) (including those added in the previous step), adding \(S\uplus p\) to \(\chi \) for each \(\langle p,S\rangle \in \varPsi _i\) and \(S\mathop {\cup \!\!\!^{{}_{\mathbf{-}}}\;}p\) to \(\chi \) for each \(\langle p,\lnot S\rangle \in \varPsi _i\).

We now illustrate these concepts by means of a simple example. Consider two knowledge bases \(\mathcal L_1\), defining travel-related concepts, including that of (tourist) \(\mathsf {Destination}\), and \(\mathcal L_2\), compiling information about wines, including a concept \(\mathsf {Region}\) identifying some major wine regions throughout the world. We wish to join these ontologies by means of rules to obtain an Mdl-program with observers that reasons about wine-related destinations. This is achieved by taking \(\mathcal P\) to be

$$\begin{aligned} \begin{array}{rll} \mathsf {wineDest}(\mathsf {Tasmania}) \leftarrow {} &{}&{} \quad (r_1) \\ \mathsf {wineDest}(\mathsf {TamarValley}) \leftarrow {} &{}&{} \quad (r_2) \\ \mathsf {wineDest}(\mathsf {Sydney}) \leftarrow {} &{}&{} \quad (r_3) \\ &{}&{}\\ \mathsf {overnight}(X) \leftarrow {} &{}\!\!\!\!\!\! DL_1[;\mathsf {hasAccommodation}](X,Y) &{} \quad (r_4) \\ \mathsf {oneDayTrip}(X) \leftarrow {} &{}\!\!\!\!\!\! DL_1[;\mathsf {Destination}](X), not \ \mathsf {overnight}(X) &{} \quad (r_5) \end{array} \end{aligned}$$

and observers \(\varLambda _2=\{\langle \mathsf {Region},\mathsf {wineDest}\rangle \}\), \(\varPsi _1=\{\langle \mathsf {wineDest},\mathsf {Destination}\rangle \}\) and \(\varLambda _1=\varPsi _2=\emptyset \).

This very simple program defines a predicate \(\mathsf {wineDest}\) with three instances obtained from rules (\(r_1\)\(r_3\)), corresponding to three wine regions that are interesting tourist destinations, together with all instances of \(\mathsf {Region}\) from \(\mathcal L_2\), which are obtained via the only element in \(\varLambda _2\). Unfolding this observer yields the rule

$$\begin{aligned} \mathsf {wineDest}(X) \leftarrow DL_2[;\mathsf {Region}](X) \qquad (r_0)\,, \end{aligned}$$

which corresponds to this intuitive semantics.

The set \(\varPsi _1\) causes \(\mathsf {Destination}\,\uplus \,\mathsf {wineDest}\) to be added to the context of every dl-atom querying \(\mathcal L_1\), extending \(\mathcal P\)’s view of \(\mathcal L_1\) – namely in rules \((r_4)\) and \((r_5)\). This causes \(\mathcal L_1\) to answer taking into account not only those instances of \(\mathsf {Destination}\) in its knowledge base, but also those instances of \(\mathsf {wineDest}\) that \(\mathcal P\) knows about (including the ones derived from \(\mathcal L_2\)).

Rule \((r_5)\) identifies the destinations that are only suitable for one-day trips. The possible destinations are obtained by querying \(\mathcal P\)’s extended view of \(\mathcal L_1\) for all instances of \(\mathsf {Destination}\). The result is then filtered using the auxiliary predicate \(\mathsf {overnight}\) defined in \((r_4)\) as the set of destinations for which some accommodation is known. This uses the role \(\mathsf {hasAccommodation}\) of \(\mathcal L_1\), where \(\mathsf {hasAccommodation}\)(\(t_1\),\(t_2\)) holds whenever \(t_1\) is a \(\mathsf {Destination}\) and \(t_2\) an accommodation facility located in \(t_1\). The reason for resorting to \((r_4)\) at all is the usual one in logic programming: the operational semantics of negation-as-failure requires all variables in a negated atom to appear in non-negated atoms in the body of the same rule. Note the impact of \(\varPsi _1\): if \(\mathsf {Destination}\) were not being updated with the information from \(\mathsf {wineDest}\), the program would not be able to infer e.g. \(\mathsf {oneDayTrip}\)(\(\mathsf {Tasmania}\)).

Mdl-programs with observers have been implemented [24] as a plugin for the dlvhex tool [25].

3 Design Patterns for Mdl-programs

In this section, we present a first set of seven design patterns for Mdl-programs, introduced in [15]. These are divided in two categories: the three elementary design patterns are the building blocks for the four more complex ones. Together, these seven patterns form a powerful set from which quite complex programs can be designed in a more structured way, simplifying the programmer’s task while at the same time yielding more flexible programs that are easier to maintain.

The presentation of each design pattern follows a similar scheme: each is presented as a pair problem/solution within the context of an Mdl-program with observers \(\langle \{\mathcal L_1,\ldots ,\mathcal L_n\},\mathcal P,\{\varLambda _1,\ldots ,\varLambda _n\},\{\varPsi _1,\ldots ,\varPsi _n\}\rangle \). In the next section, we present a large-scale example that illustrates how the seven patterns work together, complementing each other.

Elementary Design Patterns. The three basic design patterns for Mdl-programs deal with three simple tasks: transporting information from the logic program to a knowledge base and reciprocally, and giving closed-world semantics to a concept or role in one of the knowledge bases.

We first consider the case when the logic program component systematically wants to import information from a knowledge base in order to define a predicate, keeping track of changes made to the relevant concept or role.

figure a

A second scenario occurs when one of the description logics relies on the observation of a predicate from \(\mathcal P\).

figure b

The third building block addresses a very typical situation in ontology usage: a concept or role should be given closed-world semantics.

figure c

Derived Design Patterns. We now present a second set of general-purpose design patterns that can be seen as organized combinations of the previous ones, but are also useful as components of more complex patterns.

A useful variant of the Observer design pattern occurs when a description logic’s functionality relies on the observation of a predicate in a different description logic; this can be achieved by combining both the Observer Up and Observer Down patterns, thus making the logic program \(\mathcal P\) a mediator. A particular case arises when an ontology designed primarily for reasoning interacts with a knowledge base that is mostly about particular instances. This design pattern appears often in combination with Definitions with Holes below.

figure d

The next design patterns allows one to define a predicate in \(\mathcal P\) abstracting from how it is represented in the knowledge bases.

figure e

Note that Split Definitions consists of a combined application of several Observer Down, all with the same observer predicate in \(\mathcal P\). This pattern deals with a predicate that is kept as independent as possible from its definition. Instead of defining clauses, the instances are plugged in through the use of Mdl-programs with observers, thus externalizing the definition of the predicate – in the spirit of Dynamic Binding. The possibility of using different concepts or roles (possibly even from different knowledge bases) captures the essence of Polymorphism. For this reason, this pattern was originally named Polymorphic Entities [15].

The converse situation yields a different pattern, due to the way Mdl-programs are typically developed: the logic program is written to connect pre-existing knowledge bases. This pattern is particularly useful when in presence of terminological ontologies where some concepts are not defined, and captures a typical way of working with ontologies.

figure f

This pattern corresponds to the Template Method pattern of object-oriented programs [1], and to the Programming with Holes technique of [7]. In the next section we will show an example where the holes are filled in by resorting to Split Definitions.

The last design pattern in this section applies when several components of an Mdl-program contribute to the definition of a predicate.

figure g

Note that Combined Definitions is essentially different from Observer: in Observer, a predicate is defined in one component and used in others; in Combined Definitions, not only the usage, but also the definition of the predicate is split among several components, so that one must look at the whole Mdl-program to understand it. This is also part of the reason to include the negations of the predicates involved in the observers: the distributed predicate must end up with the same semantics, both in \(\mathcal P\) and in all the involved \(\mathcal L_i\)s – at least regarding named individuals.

It is possible to apply this pattern when \(\mathcal P\) does not participate in the predicate’s definition. In this case, \(\mathcal P\) is simply a mediator, and \(p^+\) and \(p^-\) can be any fresh predicate names.

This pattern was originally introduced in [15] under the name Lifting.

The application of each of the patterns proposed in this section yields localized changes to the Mdl-program: they consist of either changing dl-atoms (by means of adding pairs to \(\varPsi _i\)) or adding rules to \(\mathcal P\) (either directly, as in the case of Closed-world, or by adding pairs to \(\varLambda _i\)). In all cases, these changes are only reflected in \(\mathcal P\), and they can be divided into two or three distinct types. This is in line with the whole philosophy of dl-programs: there is an asymmetry between their components where the logic program is the orchestrator between all components as well as its façade: it is the only entity interacting with the outside world.

4 A Comprehensive Example

We now illustrate the usage of the different design patterns introduced so far by means of a more complex example.

Scenario. The software developers at WishYouWereThere travel agency decided to develop an Mdl-program to manage several of the agency’s day-to-day tasks. Currently, WishYouWereThere has two active partnerships, one with an aviation company, another with a hotel chain. Thus, the Mdl-program to be developed uses three ontologies:

  • \(\mathcal L_A\) is a generic accounting ontology for travel agencies, which is commercially available, and which contains all sorts of rules relating concepts relevant for the business. This ontology is strictly terminological, containing no specific instances of its concepts and roles.

  • \(\mathcal L_F\) is the aviation partner’s knowledge base, containing information not only about available flights between different destinations, but also about clients who have already booked flights with that company.

  • \(\mathcal L_H\) is a similar knowledge base pertaining to the hotels owned by the partner hotel chain.

One of the points to take into consideration is that the resulting Mdl-program with observers \(\langle \{\mathcal L_A,\mathcal L_F,\mathcal L_H\},\mathcal P,\{\varLambda _A,\varLambda _F,\varLambda _H\},\{\varPsi _A,\varPsi _F,\varPsi _H\}\rangle \) should be easily extended so that the travel agency can establish new partnerships, in particular with other aviation companies and hotel chains, as long as those provide their own knowledge bases. At the end of this section, we will show how the systematic use of design patterns and observers helps towards achieving this goal.

By establishing partnerships, WishYouWereThere’s client basis is extended with all the clients who have booked services of its partners. In this way, promotions made available by either partner are automatically offered to every partner’s clients, as long as the bookings are made through the travel agency. In return, the partners get publicity and more clients, since a person may be tempted to fly with their company or book their hotel due to these promotions, thereby becoming also their client.

Updating the client database. Ensuring that each partner’s clients automatically become WishYouWereThere’s clients can be achieved by noting that this is exactly the problem underlying Observer Down. Assuming \(\mathcal L_F\) and \(\mathcal L_H\) have concepts \(\mathsf {Flyer}\) and \(\mathsf {Guest}\), respectively, identifying their clients, and that the agency’s clients will be stored as a predicate \(\mathsf {client}\) in \(\mathcal P\), all that needs to be done is to register \(\mathsf {client}\) as an observer of \(\mathsf {Flyer}\) and \(\mathsf {Guest}\), which, according to the pattern, is achieved by ensuring that \(\langle \mathsf {Flyer},\mathsf {client}\rangle \in \varLambda _F\) and \(\langle \mathsf {Guest},\mathsf {client}\rangle \in \varLambda _H\).

Identifying pending payments. The designers of \(\mathcal L_A\) resorted intensively to Definitions with Holes, since many of the concepts they use can only be defined in the presence of a concrete client database. In particular, \(\mathcal L_A\) contains a role \(\mathsf {toPay}\), about which it contains no membership axioms. The information about the specific purchases a client has made and not paid so far must be collected from the partners’ knowledge bases, \(\mathcal L_F\) and \(\mathcal L_H\).

There are two ways of completing this definition. The more direct one stems from noting that \(\mathsf {toPay}\) should be an observer of adequate roles in \(\mathcal L_F\) and \(\mathcal L_H\). We will assume that these roles are \(\mathsf {payFlight}\) and \(\mathsf {payHotel}\). Applying twice Transversal Observer (which is the adequate pattern), one needs to ensure that

$$\begin{aligned} \langle \mathsf {payFlight},\mathsf {toPayF}\rangle&\in \varLambda _F&\langle \mathsf {toPayF},\mathsf {toPay}\rangle&\in \varPsi _A\\ \langle \mathsf {payHotel},\mathsf {toPayH}\rangle&\in \varLambda _H&\langle \mathsf {toPayH},\mathsf {toPay}\rangle&\in \varPsi _A\,. \end{aligned}$$

The major drawback of this solution is that it requires adding two dummy predicates to \(\mathcal P\) whose only purpose is to serve as go-between from both knowledge bases to \(\mathcal L_A\). An alternative solution is to create a single auxiliary predicate \(\mathsf {toPay}\) in \(\mathcal P\) and make \(\mathsf {toPay}\) from \(\mathcal L_A\) an observer of this predicate applying Observer Up. In turn, we use the Split Definitions pattern to connect \(\mathsf {toPay}\) to \(\mathsf {payFlight}\) and \(\mathsf {payHotel}\). The resulting Mdl-program with observers is such that:

$$\begin{aligned} \langle \mathsf {payFlight},\mathsf {toPay}\rangle&\in \varLambda _F&\langle \mathsf {payHotel},\mathsf {toPay}\rangle&\in \varLambda _H&\langle \mathsf {toPay},\mathsf {toPay}\rangle&\in \varPsi _A\,. \end{aligned}$$

As we will discuss later, this solution will also simplify the process of adding new partners to the agency.

Offering promotions. WishYouWereThere offers a number of promotions to its special clients. For example, in February the agency offers them a \(20\,\%\) discount on all purchases. Because of the partnership, the concept of special client is distributed among all partners: a client is a special client if it fulfills one of the partners’ requirements – e.g. having traveled some number of miles with the airline partner, or booked a family holiday in one of the partner’s hotels, or bought one of the agency’s pricey packages. The partnership protocol requires that each knowledge base provide a concept identifying which clients are eligible for promotions, so that the partners can change these criteria without requiring WishYouWereThere to change its program.

This is a situation where the Combined Definitions design pattern applies. Assuming that \(\mathcal L_F\) uses \(\mathsf {TopClient}\) for its special clients, \(\mathcal L_H\) uses \(\mathsf {Gold}\) and \(\mathcal P\) defines \(\mathsf {special}\), these three predicates are given the same semantics through Combined Definitions. Intuitively, this means that, in the Mdl-program’s view, all three concepts equally denote all special clients, regardless of where they originate. The application of the pattern translates to

$$\begin{aligned} \langle \mathsf {TopClient},\mathsf {special}\rangle&\in \varLambda _F&\langle \mathsf {\lnot TopClient},\mathsf {notSpecial}\rangle&\in \varLambda _F \\ \langle \mathsf {special},\mathsf {TopClient}\rangle&\in \varPsi _F&\langle \mathsf {notSpecial},\mathsf {\lnot TopClient}\rangle&\in \varPsi _F \end{aligned}$$

and four similar observers in \(\varLambda _H\) and \(\varPsi _H\), with \(\mathsf {Gold}\) in place of \(\mathsf {TopClient}\).

Furthermore, in order to determine whether a particular client is entitled to promotions, it is useful to give closed-world semantics to these predicates. Since they are all equivalent, we can do this very simply in \(\mathcal P\) by adding the rule

$$\begin{aligned} \mathsf {notSpecial}(X)\leftarrow not \ \mathsf {special}(X)\,. \end{aligned}$$

Note that we did not need to apply the Closed-world pattern because \(\mathsf {special}\) is a predicate from \(\mathcal P\), where the semantics is closed-world: the application of Combined Definitions ensures that \(\mathsf {Gold}\) and \(\mathsf {TopClient}\), being equivalent, also have closed-world semantics.

In order for one of the partner companies to make its clients eligible for special promotions, its ontology just needs to contain inclusion axioms partially characterizing special clients. For example, one could have

$$\begin{aligned} \begin{array}{rlll} \exists \mathsf {flies}.\mathsf {10000OrMore} \!\!\!\!\!\!&{}\sqsubseteq \mathsf {TopClient} &{}&{} \qquad \qquad \in \mathcal L_F \\ \mathsf {familyBooking}\!\!\!\!\!\! &{}\sqsubseteq \mathsf {Gold} &{}&{} \qquad \qquad \in \mathcal L_H \\ \mathsf {special}(X) \!\!\!\!\!\!&{}\leftarrow \mathsf {booked}(X,Y),\mathsf {expensive}(Y) &{}&{}\qquad \qquad \in \mathcal P \end{array} \end{aligned}$$

A subtle issue now appears regarding the consistency problems that may arise from the use of the Combined Definitions pattern. Since this pattern identifies concepts from different knowledge bases, it does not a priori guarantee that the resulting knowledge bases are consistent. In particular, if one of the partners grants special status to a client and another denies this status to the same client, an inconsistency will arise. More sophisticated variations of the Combined Definitions pattern can be developed to detect and avoid this kind of situation, but such a discussion is beyond the scope of this presentation.

An example of a promotion offered by WishYouWereThere to special clients would be

$$\begin{aligned} \mathsf {20\%Discount}(X) \leftarrow \mathsf {special}(X)\,. \end{aligned}$$

All special clients will benefit from this discount, regardless of who (the travel agency, the hotel partner or the aviation company) decided that they should be special clients. However, in some cases partners may want to deny their promotions to particular clients. For example, the aviation company is offering 100 bonus miles to special costumers booking a flight on a Tuesday, but this promotion does not apply to its workers. In order to allow this kind of situation, partners may define a dedicated concept identifying the non-eligible clients. Since all clients external to that partner are automatically eligible, this concept needs to have closed-world semantics so that (in our example) \(\mathcal L_F\) can include the rules

$$\begin{aligned} \mathsf {100BonusMilesWinner}&\sqsubseteq \mathsf {TopClient}\sqcap \lnot \mathsf {Blocked} \\ \mathsf {Worker}&\sqsubseteq \mathsf {Blocked} \end{aligned}$$

still giving the promotion to all clients from the other partners. Although each knowledge base can enforce this semantics in its domain, in order to extend it to other clients the Closed-World pattern must be applied, so we will have

$$\begin{aligned} \langle \mathsf {Blocked},\mathsf {blockedF}\rangle \in \varLambda _F \qquad \langle \mathsf {nonBlockedF},\lnot \mathsf {Blocked}\rangle \in \varPsi _F \\ \mathsf {nonBlockedF}(X) \leftarrow not \ \mathsf {blockedF}(X) \in \mathcal P \end{aligned}$$

Suppose that airline employee Ann qualifies for WishYouWereThere promotions because she spent three weeks in Jamaica with her husband and their five children, hence \(\mathsf {Gold}\)(\(\mathsf {Ann}\)) holds in \(\mathcal L_H\) and therefore Ann is a special client. She is therefore eligible for WishYouWereThere’s promotions, but she will still not earn the bonus miles because it is \(\mathcal L_F\) who decides whether someone gets that particular promotion, and even though \(\mathsf {TopClient(Ann)}\) holds that knowledge base will not return \(\mathsf {100BonusMilesWinner(Ann)}\). However, she will earn the \(\mathsf {20\%Discount}\), since it is offered directly by WishYouWereThere.

Adding new partnerships. We now discuss briefly how new partners can be easily added to the system later on, as this illustrates quite well the advantages of working both with design patterns and in the context of Mdl-programs with observers.

Summing up what we have so far relating to the partnerships, the sets \(\varLambda _F,\varLambda _H,\varPsi _F\) and \(\varPsi _H\) are:

$$\begin{aligned} \begin{array}{rlrl} \varLambda _F\text{: } &{}\!\!\!\!\!\! \langle \mathsf {Flyer},\mathsf {client}\rangle &{} \qquad \qquad \varLambda _H\text{: } &{}\!\!\!\!\!\! \langle \mathsf {Guest},\mathsf {client}\rangle \\ &{}\!\!\!\!\!\! \langle \mathsf {payFlight},\mathsf {toPay}\rangle &{}&{}\!\!\!\!\!\! \langle \mathsf {payHotel},\mathsf {toPay}\rangle \\ &{}\!\!\!\!\!\! \langle \mathsf {TopClient},\mathsf {special}\rangle &{}&{}\!\!\!\!\!\! \langle \mathsf {Gold},\mathsf {special}\rangle \\ &{}\!\!\!\!\!\! \langle \mathsf {\lnot TopClient},\mathsf {notSpecial}\rangle &{}&{}\!\!\!\!\!\! \langle \mathsf {\lnot Gold},\mathsf {notSpecial}\rangle \\ &{}\!\!\!\!\!\! \langle \mathsf {Blocked},\mathsf {blockedF}\rangle &{}&{}\!\!\!\!\!\! \langle \mathsf {Blocked},\mathsf {blockedH}\rangle \\ \end{array} \end{aligned}$$
$$\begin{aligned} \begin{array}{rlrl} \varPsi _F\text{: } &{}\!\!\!\!\!\! \langle \mathsf {special},\mathsf {TopClient}\rangle &{} \qquad \qquad \varPsi _H\text{: } &{}\!\!\!\!\!\! \langle \mathsf {special},\mathsf {Gold}\rangle \\ &{}\!\!\!\!\!\! \langle \mathsf {notSpecial},\mathsf {\lnot TopClient}\rangle &{}&{}\!\!\!\!\!\! \langle \mathsf {notSpecial},\mathsf {\lnot Gold}\rangle \\ &{}\!\!\!\!\!\! \langle \mathsf {nonBlockedF},\lnot \mathsf {Blocked}\rangle &{}&{}\!\!\!\!\!\! \langle \mathsf {nonBlockedH},\lnot \mathsf {Blocked}\rangle \end{array} \end{aligned}$$

Also, the application of the design patterns added the following rules to \(\mathcal P\).

$$\begin{aligned} \mathsf {nonBlockedF}(X)&\leftarrow not \ \mathsf {blockedF}(X) \\ \mathsf {nonBlockedH}(X)&\leftarrow not \ \mathsf {blockedH}(X) \\ \mathsf {notSpecial}(X)&\leftarrow not \ \mathsf {special}(X) \end{aligned}$$

The similarity between \(\varLambda _F\) and \(\varLambda _H\), and between \(\varPsi _F\) and \(\varPsi _H\), is a clear illustration of the changes required when future partners of WishYouWereThere are added to the system. Furthermore, the names they use for each concept or role are not relevant – they just need to indicate how they identify their clients, their clients’ debts, their special clients, and the clients they wish to exclude from their promotions.

5 Beyond these Patterns

The set of design patterns we presented does not by any means claim to be exhaustive or all-powerful. Design patterns for several programming paradigms have been around for more than two decades, and dozens of different patterns have been proposed and applied, often in very specific contexts. Our goal was to show how some of the most common general-purpose design patterns can be implemented within the framework of Mdl-programs, thereby illustrating the potential of this formalism. Among the more elaborate design patterns, our selection took into account the ones that can be more naturally formalized using Mdl-programs with observers. In this section we explore some limitations and discuss future directions for our work.

In a practical context, it is not uncommon to have a function in \(\mathcal P\) whose definition is unstable in the sense that it may vary, for example rotating cyclically among several possibilities. Also, there are cases where one foresees possible future variations which are not contemplated in the existing requirements. The following pattern provides a clean way to implement such functions in a way that minimizes undesirable impact on the other elements of the program.

figure h

Different applications of this pattern may share the same dedicated knowledge base \(\mathcal L_I\), since each of them only looks at a particular concept or role.

The Indirection design pattern captures some aspects of the principle of Protected Variations in object-oriented programming [5], which is a root principle motivating most of the mechanisms and patterns in programming and design that provide flexibility and protection from variations. This pattern was not originally introduced in [15].

As a particular case, it may happen that a component of a system is not known or available at the time of implementation of others, yet it is necessary to query it. A way to get around this is to use a prototype knowledge base that will later on be connected to the concrete component in a straightforward way.

The same problem may also arise if one wishes to be able to replace a knowledge base with another with a similar purpose, but whose concept and role names may be different.

Both of these situations reflect another aspect of the same principle of Protected Variations mentioned above, but now the point of variation in \(\mathcal {KB}\) that \(\mathcal P\) is being protected from lies in one of the knowledge bases and not in \(\mathcal P\) itself.

figure i

There is one important characteristic of this implementation of the usual Adapter design pattern: the Mdl-program syntax for local extensions to dl-queries only works in the particular case where the query is over a concept or role being directly extended. Because all queries go through the interface knowledge base, where no axioms exist, any other extensions are lost. The following example illustrates this situation.

Consider an interface \(\mathcal L_I\) specifying two concepts \(P\) and \(Q\), which are made concrete in \(\mathcal L_C\) as \(A\) and \(B\). Furthermore, \(\mathcal L_C\) also contains the inclusion axiom \(A\sqsubseteq B\). Finally, \(\mathcal P\) contains the single fact \(\mathsf {thisIsTrue}(\mathsf {ofMe})\). In \(\mathcal P\), the direct query \(DL_C[A\uplus \mathsf {thisIsTrue};B](X)\) returns the answer \(X=\mathsf {ofMe}\), since \(\mathcal L_C\) is extended with \(A(\mathsf {of Me})\) in the context of this query. However, the corresponding indirect query (i.e. the same query, but passing through the adapter)

$$\begin{aligned} DL_I[P\uplus p^+,P\mathop {\cup \!\!\!^{{}_{\mathbf{-}}}\;}p^-,Q\uplus q^+,Q\mathop {\cup \!\!\!^{{}_{\mathbf{-}}}\;}q^-,P\uplus \mathsf {thisIsTrue};Q](X) \end{aligned}$$

after extending \(\mathcal P\) with the rules

$$\begin{aligned} p^+(X)&\leftarrow DL_C[;A](X)&q^+(X)&\leftarrow DL_C[;B](X) \\ p^-(X)&\leftarrow DL_C[;\lnot A](X)&q^-(X)&\leftarrow DL_C[;\lnot B](X) \end{aligned}$$

– introduced by the concretization of the observer sets – returns no answer, since \(\mathcal L_I\) only knows the facts about \(B\) that are directly given by \(\mathcal L_C\) through \(q^+\).

Note, however, that the dl-atom \(DL_C[A\uplus \mathsf {thisIsTrue};A](X)\) is equivalent to

$$DL_I[P\uplus p^+,P\mathop {\cup \!\!\!^{{}_{\mathbf{-}}}\;}p^-,Q\uplus q^+,Q\mathop {\cup \!\!\!^{{}_{\mathbf{-}}}\;}q^-,P\uplus \mathsf {thisIsTrue};P](X)\,,$$

since the query is directly on the concept whose extent was altered. This is a restriction with respect to the full power of Mdl-programs; but we see it as a feature of the Adapter design pattern. Should a context arise where such flexibility is essential, then this is not the right design pattern to apply. In practice, situations where a Adapter is applicable are common enough to make it a useful pattern.

A more problematic situation arises when one wants to control or restrict access to a resource, for example a database containing sensitive information – a problem typically addressed by means of a proxy. In practice, this is not very different from the Adapter design pattern – but Adapter is an algorithm-free pattern that just defines interfaces, whereas an entity implementing Proxy is expected to do some processing before passing on the information it receives.

An implementation along the lines we have followed so far would explore the possibility of a proxy knowledge base to serve as a mediator between two components. In the setting of Mdl-programs, however, this is actually not possible to achieve directly, since all queries must go through the logical program. The only other option is to encode the proxy in the logic program itself, forcing every dl-query to the protected resource to be immediately preceded by some atoms implementing the proxy – which from the Proxy design pattern perspective is not completely satisfactory, since the person who develops the logic program can access the implementation of the proxy.

There would be ways to go around these problems, namely by extending Mdl-programs with appropriate syntactic constructions besides observers. Indeed, our motivation for defining Mdl-programs with observers was, primarily, to guarantee that all dl-queries were appropriately extended, even the ones that were written after deciding that a concept or role should be observing a predicate. As it turned out, this construction is powerful enough to allow for elegant implementations of all the design patterns discussed earlier. There is an aspect that cannot be overstressed: the sets \(\varLambda _i\) and \(\varPsi _i\) are syntactic sugar. As such, they do not add to the expressive power of Mdl-programs, but they substantially increase their legibility and internal structure. By working with an Mdl-program with observers, one can more easily understand the core of the program (which is the logic program \(\mathcal P\)) without being disturbed by the presence of myriads of rules that connect \(\mathcal P\) with the several knowledge bases. In particular, most of the design patterns we presented can be expressed simply as adding specific pairs to carefully chosen observer sets \(\varLambda _i\) and \(\varPsi _i\) – yielding a clean program that is also very easy to maintain and extend. At the end of the day, though, the Mdl-program with observers simply translates into an Mdl-program.

To deal with a full Adapter or Proxy, one would have to extend the syntax of Mdl-programs in a non-conservative way; but doing this would remove their simplicity, which was the main motivation for using them in the first place. Therefore, this section can be summarized as follows: the design patterns here introduced allow one to write Mdl-programs in a clean and elegant way, thereby obtaining programs of a better quality than ad hoc designed solutions. If one needs to go beyond the power of Mdl-programs, namely to implement a full-fledged proxy, then one should not use them at all, but rather move to a more powerful formalism.

6 Conclusions

The purpose of the present study, at this stage, is to show that design patterns have a place in the world of the Semantic Web. One can foresee a future where there is a widespread usage of systems combining description logics with rules, and the availability of systematic design methodologies is a key ingredient to making this future a reality.

This paper extends the original presentation in [15] by discussing an initial set of design patterns for Mdl-programs, together with a large-scale example illustrating their application, and showing the inherent limitations of this programming framework.

An aspect that will have to be addressed in the future relates to the practical issues of the usage of design patterns. Ad hoc solutions to specific problems may be more efficient than the application of systematic methods, but they tend to yield less generalizable and less extensible software applications. Also, the use of observers (essential to many of the patterns proposed) introduces higher complexity, especially when non-stratified negation is involved. It is important to understand the compromise between efficiency and quality obtained by a systematic use of design patterns by means of a practical evaluation using the prototype implementation of Mdl-programs within dlvhex [24].

The mechanisms herein discussed can be applied to multi-context systems, in view of the similarities between these and Mdl-programs. A preliminary study of this connection has been undertaken in [13].