开发者

Help with seeming contradiction of two concepts - oop and n-tier development

Not so Newbie to oop here, but still making the transition from structured programming. I'm trying to reconcile the concept of a self contained object with n-tier programming. Seems to me you can have one or the other but not both.

Please tell me where I am going wrong here with n-tier-architecture. Say I want to work with a bunch of People. I create a 'Person' class in an Entities Tier with FN, LN, BDay, etc. I will use this Person class to represent one person in my all the layers -UI, Business and DataAccess. But I generally can't put any useful methods into 'Person' because then I will be crossing over tier boundries.

So I end up creating a UiPersonClass, a BusinessPersonClass, and a DataAccessPersonClass each with a 'Person' class member. Then I end up creating a constructor for each tier class the accepts a 'Person' class parameter (coming from the other layers) and sets it to this.Person. And I new up a adjacent layer PersonClass with this.Person as a parameter. etc.

This feels really wrong, but how else are you supposed to do this?

If I was using one layer only, I could have a 'Person' class with methods that popul开发者_开发百科ate ui controls, process the info, and save and retrieve data from the database, all in one place, all in one "Object" .


One of the problems that I had when first learning Object Oriented Programming was that the examples shown to illustrate concepts were so basic that they made no sense.

Why add piles of seemingly needless complexity when there were much easier and more straightforward ways to program solutions? Why code interfaces that didn’t seem to add anything but more programming work and confusion? Why create multiple Layers when all I was doing was passing unchanged values down the chain?

A lot of it started to make sense when I started doing things that were more complicated. In the case that you give here it seems that the data does not change as it is passed from one layer to the next. So having multiple layers doesn’t seem to make sense. However, imagine a more usual scenario where the way your data is stored in the database does not match what is displayed in the UI.

Then the example you give is not so straightforward.

I find it helps to think of it this way: The BusinessLayer translates between the data in the database and the data in the UI.

I usually have a basically one to one correspondence between the data in the database and the objects which are created in the DataLayer and passed back and forth to the BusinessLayer.

I have a basically one to one correspondence between the data that is passed back and from the BusinessLayer to the UI and what is displayed in the UI.

Those objects are usually very different.

The BusinessLayer is where the translation happens. This is whether retrieving data from the database to display in the UI, or collecting data entered in the UI and storing it in the database.

When things start to get complicated, it turns out is is much easier to do things this way. Especially when changes are made to the application


In my opinion your first concept about having a Person class to be shared between layers is correct. You can abstract it a little bit more to, say, add an interface IPerson so that any object representing a Person will implement this interface.

As for the separation in the layers goes, I do not think that you should have a BusinessPersonClass and a DataAccessPersonClass. Instead I believe you should aim for the following: Your business layer will operate on IPerson type: it does not care if the object is of type Person or Salesman for example, it just knows that it expects an IPerson type of object. Moreover, the business layer is where all the business rules go (naturally) - validation, etc. Those business rules I believe should not be a part of the Person object. In fact the Person object should only be concerned with atomic operations and properties of one person - in other words it should not contain any business or data access logic.

Same goes for the data access layer -- it operates on IPerson, returns IPerson(s), modifies IPerson(s). The data access layer is only concerned with CRUD operations, or how does it add,get,update, delete an IPerson from/to the data source.

Now with the UI, I think it is a little bit more different. There normally you would implement your ViewModels (UIModels) and some simple validation rules (not concerning business logic but validity of input). I can think of two approaches here:

1) Every UI view model concerning a Person implements IPerson. In that way you ensure consistency over the tiers and you can easily work with this data.

2) Every UI view model contains a property of type IPerson (as you specified above). This is also a nice approach in my opinion because it makes it easier for communication between the UI and business layer.

In summary, here is what I think:

UI layer Has a reference to a IPerson in some way or the other. Implements basic validation rules -- validity of input (is the inputted Email really an email). Then, the UI passes this IPerson object to the business layer for the business rules and operations.

Business layer Takes an IPerson object, validates it using business rules (does this email already exist in the system). Performs some more business layer operations, may be, and calls the data access layer, for example, to create an entity. In this example the business layer will call a Create function and pass it an object of type IPerson.

Data access layer CRUD operations on atomic entities - IPerson, IProduct, etc.

Hope this helps.


I think your mistake is that you have assumed that Person should be able to do everything itself (know how to load itself, how to render itself on the screen, etc.). If you take the radical route of this thought, you will end up Person containing screen drivers (because it needs to know how to render itself on the screen!).

As you have already got the layers (persistence, rendering, etc.) defined you should think of responsibilities of those layers instead. E.g. you can have GraphicsSystem accepting Person and rendering it on the screen. Or PersonDAO which is responsible for retrieving Person from the database. Or PersonPrintService which knows how to print, and so on.

Generally speaking, when modelling data objects (such as Person), you should concentrate on the data that travels across the layers and not functionality itself.

The approach you took (BusinessPersonClass, DataAccessPersonClass, etc.) is called Decorator pattern. The situations when to apply it differ, but to me the best example of this pattern is Java's OutputStream class which can be wrapped into BufferedOutputStream and then again wrapped into FileOutputStream, etc.

I don't know what logic BusinessPerson adds on top of Person but to me it looks like that if there are some people who are "business people", you're better off making BusinessPerson extend Person.

I can't make any further comments as I don't know your problem area.


ok, i may be redundant here but i think there is some point left:


Sorry, but my english sucks. But I will do my best. :(


You are using n-tier-architecture. This means you application is Big (tiers and layers only helps if your application is big enough ). N-tier grants the ability to isolate some things. Example: your DB changes. Dont mind how, it just changes. Your DAO layer may change, but your Bussiness layer stay the same. Boy, this is huge. If you have an really big application this is a very desirable point.

This is what n-tier means. It isolates one layer from another. Your UI changes do not affect your data layer and so on. Keep it in mind. Ok, next step.

Now, you is using n-tier, and each tier/layer is actually a really big 'application'. Your Business layer (or any other layer) does a lot of things and have its owns objects and domain. Here, inside a layer, you use oop completely. Inside a tier/layer, your objects only care about your tier/layer domain, you can relate object with its own responsability. In a business layer Person only does what Business Person does. In UI your equivalent Person only know how to render its data or whatever you want Person does in UI.

Ok, one more point to clarification.

Between Layers you have an interface. Not an Java Interface, an interface like a contract of operation. In this boundry you dont use oop. You is passing DATA, not objects, not logic, only DATA. What it means? It means whatever you pass through your layer/tiers its not an true oop object (or, its not supposed to be). Why? Because you dont want to have an object having more than one responsability right?

Can you see my point here?

You may have a Person class. But the Person class in Business is diferent in DAO layer and diferent for UI UNLESS Person class is just data traveling between layers.

n-tiers and oop is not contradictory or correspondent. They are not the same type of things to compare.


I tend to think in terms of verbs; UI tiers are filled with classes that render, view, scroll, click, and display. Data tiers save, load, update, and delete. The business layer is all about objects that mirror the real world in some way. So your app might contain;

UI: PersonView, PersonEditPage, NewPersonForm

Business: Person, Birthday, Address, Salesman, Employee

Data: Connection, Database, Table, Column

So a typical sequence of operations in a web app might go;

(screen)      UI        Business        Data      (storage)

Form/Page  PersonView   Organization    Connection     DB
   |           |             |                |         |
   |--type-+   |             |                |         |
   |       |   |             |                |         |
   |<------+   |             |                |         |
   |           |             |                |         |
   |---click-->|             |                |         |
   |           |--find(#3)-->|                |         |
   |           |             |-LoadPerson(3)->X         |
   |           |             |                X-select->X
   |           |             |                X         X
   |           |             |                X<--------X
   |           |             |<-----Person----|         |
   |           |<---Person---|                |         |
   |<---HTML---|             |                |         |
   |           |             |                |         |

What's happening here?

First, the user is typing and clicking in the browser. When they click, the PersonView object, representing the appearance of a person on the web, intercepts the call. In pseudocode:

onLinkClick() {
  personView.FindPerson(id:3);
  rendertoHtml();
}

PersonView now needs to find a person, so it asks the business layer; it asks an object called 'Organization' to find a particular person. Eg;

PersonView.FindPerson(id) {
  organization.FindPerson(id)
}

Note that it asks without knowing about databases or XML or web servcies; it just asks the business layer directly, 'can you find person #3?'. So the UI layer is isolated from the data layer.

The business layer (Organization) then goes to the data layer, and asks it to load the person record.

Organization.FindPerson(id) {
  connection.LoadPerson(id);
}

Again, it isn't making data-specific requests. Now we get to the connection, which does know about the specific details of the data layer;

Connection.LoadPerson(id) {
  connectDb();
  execute("SELECT name, dob FROM Person WHERE id =@id");
  return new Person(name, dob);
}

So the connection definitely does know about the particular storage mechanism (SQL) but has no knowledge about the UI layer that called it. It returns a Person object, and Organization passes it back to PersonView, which then knows how to generate HTML.

How does this structure help? Imagine that you wanted to migrate from SQL to XML files for your data storage. You could create a different implementation of Connection which used xpath, like so;

Connection.LoadPerson(id) {
  LoadXml();
  SelectNode("//person[@id=$(ID)]");
  return new Person(xml['name'], xml['dob']);
}

and the whole system would contine working. You can also change the UI layer (from web to windows, for instance) and neither the business layer nor the data layer need to be rewritten.


Since I have just upvoted everyone here, I'm going to share my perspective here too and talk a little more on the testing equation. :)

I find it interesting that I have come from 0-tier (putting everything in JSP files) to 2-tier (servlet-JSP) to god-knows-how-many-tiers right now. What I can tell you from my experience is not to get too crazy in coming up with extra tiers if you don't need it. You may read programming books requiring you to have this layer or that layer, but it all boils down to the complexity of your current project.

The pros of having tiers is to promote layer of abstractions... for example, if you decide to swap out your Struts' front-end with Spring MVC or JSF framework, then it will not cause unwanted negative ripple effects to the rest of your implementations (database, security, logging, etc). The cons of having too many tiers is you unwantedly introduce more complexity into your project. Everything is too abstracted out that you lose track which tier does what anymore. Further, if you don't implement the tiers properly, you are going to end up with tight coupling between the tiers and you are going to find unit testing to be extremely difficult and frustrating. This is because in order for you to unit test component A, you will have to first initialize 10 other components from other tiers... and finally, you end up not doing any unit testing because everytime you change something in your code, you will have to fix all your testcases. I have been through that mistake myself.

To keep things simple for now, you need the following tiers:-

  • UI: This is your viewer. It could be JSP, HTML, Freemarker etc.
  • Model: This holds the Person class. It contains all the properties of the person and getters/setters method. That's it... no business logic in it. It's POJO. You use this to pass around between tiers.
  • Controller: This is your "air traffic controller". It handles the user request, it knows what services to call to get the job done yet there are minimal computations in here. Think of controller as your boss. The boss always asks the employees to do this and that and finally, the boss takes the result and represent it to the user through the UI.
  • Service: This is where you have your PersonService. It handles all the services related to the person, for example creating new patient, finding person by last name, etc. Your service is the only one that can communicate against the database. It will map all the retrieved person information from the database into the Person model and returns it to the controller. If your project scope is big, perhaps you want to introduce a DAO tier to handle all database related communication, but for now, a DAO tier is not needed unless you can justify a need for it.

By looking at this... the first 3 tiers is your MVC, which is very commonly used in the web application development. The Service tier is introduced here to promote code reusability between the Controllers. For example, you have many multiple controllers that may want to utilize PersonService to perform CRUD operation on a person.

From here, unit testing becomes very simple. Since Service is an independent tier and it doesn't rely on any other tiers, you can easily write testcases to test your entire Service class. Once you have tested all your service APIs, then testing the Controller is going to be straightforward because now, you can safely mock the Service class in the controller to perform your "happy-path" and "not-so-happy-path" testings. Model doesn't really require any testing, because it's just a POJO. This way, you can easily achieve 95% test coverage.

There... all happy and done. :)


If I may add my USD 0.02 here, probably slightly off the very question, but related to the feeling of doing things the wrong way.

It is my strong belief that every technology serves its purpose, and as much as there's no unified field theory in physics, there's no miracle technology that could cover all sorts of architectural or other problems in software development.

It is also my strong belief that the metaphorical meaning of object as an abstract incarnation of a real world entity throughout the application is often, though not always, quite misleading.

Every tier in multitier development has its purpose, and therefore can benefit from using specific structuring or optimization techniques. Mixing them together only appears to create consistent architecture, where in fact it often does not.

I strongly believe that database architecture (having the best structure for storing the data) need match neither the class/object architecture (having the best structure for actions on data), nor presentation logic architecture (having the best structure for displaying the result of actions).

They are all different, and should be treated that way.


There is nothing wrong with having different models for different "tiers" and mapping between them. In fact, many patterns encourage this.

Take, for example, MVVM (Model - View - ViewModel). The idea is there is a business (or domain) model that contains object state that is useful for your business logic. Think of this as the model for the business tier. When bound, you may have to combine models or prune models of information not needed for a particular view. This is a view model and corresponds to the UI tier. You also have a persistance mechanism that has its own representation, most often identical, or at least very close, to the relational database the data comes from (although you can persist to other types).

In a very simple implementation, you can muddy the waters by having a single model describe all tiers. The main issue with this type of model is it ends up very relational and not OO. In addition, it focuses on the needs of the persistance mechanism and not of the actual domain you are working in (ie, the business needs of the application).

I, personally, have altered the view to a circle representing the "application", which is most akin to the business layer in the typical n-tier diagram. I then can then swap out persistance mechanisms (database, XML file, etc) without impacting the core "application" code. I can also swap out different UIs without changing the core application.

Back to your question. There are cases where the UI "model" needs to be different from the business model. In these cases, I create a means of translating the model back and forth (mapping is another good word here). The same basic pattern works between the persistance and business "layers".

Now, someone mentioned service as a separate concept. From the perpsective of an application as business logic, I find this problematic. A Service, when viewed from the core of the business flow, is a persistance mechanism. Encapsulated in the service is a domain model that is used by the service "application", but when viewed from your "application", you most often use a service to persist objects. Going back to the separation of the business "layer" and data/persist "layer", you should be able to swap a service for a database without a huge amount of effort.

This is just my two cents and your mileage may vary.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜