Другой подход заключается в использовании модели push, а не модели pull. Как правило, вам нужны разные средства форматирования, потому что вы нарушаете инкапсуляцию и получаете что-то вроде:
class TruckXMLFormatter implements VehicleXMLFormatter {
public void format (XMLStream xml, Vehicle vehicle) {
Truck truck = (Truck)vehicle;
xml.beginElement("truck", NS).
attribute("name", truck.getName()).
attribute("cost", truck.getCost()).
endElement();
...
, куда вы извлекаете данные из определенного типа в форматтер.
Вместо этого создайте независимый от формата приемник данных и инвертируйте поток так, чтобы определенный тип отправлял данные в приемник
class Truck implements Vehicle {
public DataSink inspect ( DataSink out ) {
if ( out.begin("truck", this) ) {
// begin returns boolean to let the sink ignore this object
// allowing for cyclic graphs.
out.property("name", name).
property("cost", cost).
end(this);
}
return out;
}
...
Это означает, что у вас все еще есть инкапсулированные данные, и вы просто подаете помеченные данные в приемник. Затем приемник XML может игнорировать определенные части данных, возможно, переупорядочить некоторые из них и написать XML. Он может даже делегировать различные стратегии поглощения внутренне. Но раковина не обязательно должна заботиться о типе транспортного средства, только о том, как представлять данные в каком-либо формате. Использование встроенных глобальных идентификаторов вместо встроенных строк помогает снизить стоимость вычислений (имеет значение только в том случае, если вы пишете ASN.1 или другие жесткие форматы).