jeudi 2 avril 2015

How to overdesign a shell

Yesterday I stumpled upon this question, it asks about creating a OO shell with use of the command design pattern.


It made me curious, because I always hated my shells (a cascade of ifs and elses). I answered the question with a fully working example.


Here's a little excerpt that depicts the main idea



private final Writer writer = new BufferedWriter(new OutputStreamWriter(
System.out));
private boolean quit = false;

private Map<String, Command> commands = new HashMap<>();
{
commands.put("create", new CreateChat(this));
commands.put("join", new JoinChat(this));
commands.put("exit", new ExitCommand(this));
}

public void run() throws IOException {
try (Scanner s = new Scanner(System.in)) {
writer.write("> ");
writer.flush();
while (!quit && s.hasNextLine()) {
String input = s.nextLine().trim();

// get or default is java8, alternatively you could check for null
Command command = commands.getOrDefault(input, new UnknownCommand(this, input));
command.execute();

if (!quit)
writer.write("> ");
writer.flush();
}
}
}


Still, I'm not completely pleased. I'd prefer a declarative solution, where you can specify the commands and map them to a appropiate instance of the Command interface.


My problem herin is, that I'm not sure how to handle complex inputs, e.g.


collect sample <sequence<double>>


collect sample 1.0 1.5 1.33 1.45


or class teacher <name> pupils <name> <name> ...


I could hand the command instances a Reader object, but I feel it is not their responsibility to read their own input. They should have a CommandParameter object etc.


But I'm not sure how to design it. I once implemented a binary protocol parsing library which enabled the user to define the protocl via xml, that seems like a solution (but a complex one).


Another question is, how do I tell the user when - and where exactly - formatting errors occur, in case the CommandParameter can't be created?




TL;DR


So to finish with a clear question: What are the design patterns one can utilize to create a clean shell (in a declarative way), while respecting all the common clean code rules (separation of concerns, single responsibility principle, etc.).


BOM (bill of material) proper representation in a class

I need an expert advice on how to represent BOM concept in a class. So far in my application I had only one Equipment. But now there is a CR for BOMintegration. So i have to modify my existing Equipment class. How can I represent BOM child and parent relationship in proper way?


I have one suggestion.



class Equipment {

List <Equipment> childEquipmentList;

public void addChildEquipment(EREquipmentVO childObject) {
if(null == childEquipmentList) childEquipmentList = new ArrayList<EREquipmentVO>();
this.childEquipmentList.add(childObject);
}

public List<EREquipmentVO> getChildEquipmentList() {
return childEquipmentList;
}

}


Want to know is there any other better way to implement BOM relationship in Equipment object.


Code for following pattern for the value of n

Code for following pattern for the value of n.



1*2*3*4*17*18*19*20*
--5*6*7*14*15*16
----8*9*12*13*
------10*11*

mercredi 1 avril 2015

"Invitation to Event" feature in Rails w/ best practices

I'm trying to write more efficient and readable code in Rails, and right now I'm working on an app which requires a type of invitation system. In this app I have an Event model. Users can create events. User's can request invitations from the owner of an event, and the owner of an event can offer invitations to users.


Invitation Offer



  1. Alex creates an event.

  2. Alex offers Mike an invitation to his event.

  3. Mike rejects Alex's invitation.


Invitation Request



  1. Bob creates an event.

  2. John requests an invite to Bob's event.

  3. Bob accepts John's invite request.


Once the owner accepts an invitation request, that user must be linked to the Event model somehow, possibly through a Guest model.


If I can get some advice on invitation requests, I think I can figure out the 'offer an invitation' side of the problem on my own. Here's what I have so far:


Schema



create_table "invitations", force: :cascade do |t|
t.integer "inviter_id" #event owner (maybe change field name)
t.integer "invitee_id" #guest
t.text "greeting"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "is_request"
t.string "status" #set to accepted, waiting, or rejected
t.integer "event_id"
end


Model



class Invitation < ActiveRecord::Base

after_create :send_notification

belongs_to :event
belongs_to :invitee, class_name: 'User'
belongs_to :inviter, class_name: 'User'

def inviter
event.user
end

def send_notification
if self.is_request?
self.inviter_notification #notify event owner of invitation request
else
self.invitee_notification #notify potential guest of invitation
end
end

#using Mailboxer to send notifications
def invitee_notification
subject = "#{inviter.name} has invited you to attend #{event.title}"
body = self.greeting
invitee.notify(subject, body, self)
end

def inviter_notification
subject = "#{invitee.name} has requested an invitation to #{event.title}"
body = self.greeting
inviter.notify(subject, body, self)
end

end


Controller



def create
@invitation = Invitation.new
#TODO invitation_params method not working for some reason
@invitation.invitee_id = params[:invitee_id]
@invitation.meetup_id = params[:meetup_id]
@invitation.message = params[:message]
@invitation.is_request = params[:is_request]

respond_to do |format|
if @invitation.save
format.html { redirect_to :back, notice: 'Invitation was successfully sent.' }
else
format.html { redirect_to session.delete(:return_to), notice: 'Invitation failed to send.' }
end
end
end


What's missing is a way for users to actually accept and deny requests and offers. How could I do that RESTfully?


I'm not sure if a single Invitation resource is the best way to go, because of the separate Offer and Request stories. I was thinking about renaming the resource to OfferInvitation and creating a RequestInvitation resource. Requesting invites is actually more important than offering, and I've kind of just shoved the functionality in there with the is_request? field.


I'd appreciate any advice on how this code could be improved, and how I might implement the accepting and denying of requests and offers, and then linking the resulting guest to the Event model. Do you think a Guests table is necessary (so that I can do myEvent.guests), or would it be better to create a scope to find accepted Invitations?


I'm pretty new to best-practices and refactoring code, so having a good solution to this would help me a lot with future coding. Hope this wasn't too longwinded.


Thanks.


Creating and managing unique instances of class in Java that can be accessed by different users

I have a programming problem that I want to know if it can be solved using Java design techniques. I have class Service and I have a class Client. A client requests a service and if it's not already existing, then it will be created (i.e. new service object). If the service has been created (i.e by a different client or even the same client), then the Service class will not create a new object. Instead, the client can be added to the service (if not already added). Other fields and methods of the Service class will be applied to clients of the same service.



public class Service {

private String service;
private ArrayList<Integer> clients;
//.... other field

public Service (String s){
this.service = s;
clients = new ArrayList<>;
}

public void addClient(int c){
clients.add(c);
}



//..other methods

}

public class Client {

private int clientID;
private ArrayList<String> services;

public Client(int id){
clientID = id;
services = new ArrayList<>;
}

public void addService(String s){
services.add(s);
}

public void requestService() {
for(int i=0; i<services.size();i++)
Service s = new Service(services.get(i));
}

}


The problem with the above approach is that new service objects with the same service would be created by different clients.


I'm currently reading up on on static factory. As far as my research goes:



public class Service(){

private Service(){
}

public static Service createService(String service){
if (/*service doesn't exist*/)
return new Serivce();
else
return null;
}
//....
}


This above code would prevent creating a new object instance. However, if service already exists and therefore returns null, then a new client cannot join (or use) that particular service.


Design Pattern Command for Switch java

I want to use design pattern for this switch - case code. I tried to use the command pattern, but I could not understand how(I was programming only 2 for months) I wrote this program to learn how to better program.


My code:



public class Server {
private static final String READ_NEW_MESSAGES = "read new mes";
private static final String SEND_PRIVATE_MESSAGES = "send mes";
private static final String JOIN_CHAT = "find chat";
private static final String CREATE_CHAT = "chating";
private static final String QUIT = "quit";
private static final String EXIT = "exit";
private static final String REGISTRATION = "reg";
private static final String CREATE_PRIVATE_CHAT = "priv chat";
private static final String CONNECT_TO_PRIVATE_CHAT = "connect pm";
private static final String START_CHAT = "Start";
private Populator<PrivateMessage> privateMessagePopulator;
private Populator<Registration> registrationPopulator;
private Populator<Message> messagePopulator;
private Populator<PrivateChat> privateChatPopulator;
private Populator<Chat> publicChatPopulator;
private List<PrivateMessage> privateMessages;
private BufferedReader reader;
private String currentUser;
private Set<String> users;
private static Logger log = Logger.getLogger(Server.class.getName());
private List<Chat> chats;
private String password;
private Set<Registration> registration;
private List<PrivateChat> pmChat;
private String chatName;

public Server() {
server();
}

public void server() {
reader = new BufferedReader(new InputStreamReader(System.in));
privateMessages = new ArrayList<PrivateMessage>();
users = new HashSet<String>();
chats = new ArrayList<Chat>();
privateMessagePopulator = new PrivateMessagePopulator();
privateChatPopulator = new PrivateChatPopulator();
publicChatPopulator = new PublicChatPopulator();
messagePopulator = new MessagePopulator();
registrationPopulator = new RegistratorPopulator();
registration = new HashSet<Registration>();
pmChat = new ArrayList<PrivateChat>();
}

public void start() {
String decition = "";

while (true) {
try {
registrationOrLogin();
} catch (IOException e1) {
e1.printStackTrace();
}

while (decition != QUIT) {

System.out.println("Create a chat - chating");
System.out.println("Join the chat - find chat");
System.out.println("Send private message - send mes");
System.out.println("Read new messages - new mes");
System.out.println("Quit - quit");
System.out.println("Create private chat - priv chat");
System.out.println("Connect to private chat - connect pm");

try {
decition = reader.readLine();
switch (decition) {
case CREATE_PRIVATE_CHAT:
createPrivateChat();
break;
case CREATE_CHAT:
createChat();
break;
case JOIN_CHAT:
joinChat();
break;
case SEND_PRIVATE_MESSAGES:
sendPrivateMessage();
break;
case READ_NEW_MESSAGES:
showNewMessages();
break;
case QUIT:
logout();
break;
case REGISTRATION:
registration();
break;
case CONNECT_TO_PRIVATE_CHAT:
joinToPrivateChat();
break;

default:
break;
}
} catch (IOException e) {
log.warning("Error while reading decition from keyboard. "
+ e.getMessage());
}
}
}
}

private void sendPrivateMessage() throws IOException {
PrivateMessage privateMessage = privateMessagePopulator.populate();
privateMessage.setSenderName(currentUser);
privateMessages.add(privateMessage);
}

private void joinChat() throws IOException {
System.out.println("Exist public chat");
for (Chat chat : chats) {
System.out.println(chat.getChatName());
}
System.out.println("Enter the name of chat you wish to join");
chatName = reader.readLine();
for (Chat chat : chats) {
if (chatName.equals(chat.getChatName())) {
for (Message mes : chat.getMessages()) {
System.out.println(mes.getSenderName() + ": "
+ mes.getContent());
}
publicComunication(chat);
}
}
}

private boolean hasNewMessages() {
boolean result = false;
for (PrivateMessage privateMessage : privateMessages) {
if (currentUser.equals(privateMessage.getReceiverName())) {
result = true;
}
}

for (PrivateChat pm : pmChat) {
if (pm.getAddUserName().equals(currentUser)) {
result = true;
}
}
return result;
}

private void showNewMessages() {
if (hasNewMessages()) {
for (PrivateMessage privateMessage : privateMessages) {
if (currentUser.equals(privateMessage.getReceiverName())
&& MessageStatus.DIDNT_READ.equals(privateMessage
.getStatus())) {
System.out.println(privateMessage.getSenderName() + ": "
+ privateMessage.getContent());

}
privateMessage.setStatus(MessageStatus.ALREADY_READ);
}
}

if (hasNewMessages()) {
for (PrivateChat pm : pmChat) {
for (Message message : pm.getMessages()) {
if (pm.getAddUserName().equals(currentUser)) {
System.out.println(message.getSenderName() + ": "
+ message.getContent());
}
}
}
} else {
System.out.println("you don't have new message ");
}
}

private void registrationOrLogin() throws IOException {
String logOrReg;
System.out
.println("Hi,if you already have account - 1,\nIf you would like to register - 2");
logOrReg = reader.readLine();
if (logOrReg.equals("1")) {
login();
} else if (logOrReg.equals("2")) {
registration();
} else {
registrationOrLogin();
}
}

private boolean hasUser() {
boolean result = false;
for (Registration reg : registration) {
if (currentUser.equals(reg.getUserName())
&& password.equals(reg.getUserPassword())) {
result = true;
}
}
return result;
}

private void login() throws IOException {
System.out.println("Please,enter user name and password ");
currentUser = reader.readLine();
password = reader.readLine();
if (hasUser()) {
System.out.println("You already logged in system");
} else {
System.out.println("Wrong user name or password");
registrationOrLogin();
}
}

private void logout() throws IOException {
currentUser = null;
password = null;
registrationOrLogin();

}

private void createChat() throws IOException {
Chat chat = new Chat();
chat = publicChatPopulator.populate();
publicComunication(chat);
chats.add(chat);
}

private void joinToPrivateChat() throws IOException {
for (PrivateChat pm : pmChat) {
for (String user : pm.getUsers()) {
if (user.equals(currentUser)) {
System.out.println(pm.getChatName());
}
}
}
System.out.println("Enter the name of the chat you wish to join");
chatName = reader.readLine();
for (PrivateChat pm : pmChat) {

if (chatName.equals(pm.getChatName())) {

for (Message message : pm.getMessages()) {
System.out.println(message.getSenderName() + " "
+ message.getContent());
}
privateComunication(pm);
}

}

}

private void createPrivateChat() throws IOException {
PrivateChat privateChat = new PrivateChat();
Set<String> chatUsers = new HashSet<String>();
privateChat = privateChatPopulator.populate();
while (true) {
privateChat.setAddUserName(reader.readLine());
chatUsers.add(privateChat.getAddUserName());
privateChat.setUsers(chatUsers);
for (String user : users) {
if (user.equals(privateChat.getAddUserName())) {
System.out.println("you add too chat user - "
+ privateChat.getAddUserName());
}
}
if (privateChat.getAddUserName().equals(START_CHAT)) {
break;
}

}
privateComunication(privateChat);
pmChat.add(privateChat);
}

private void registration() throws IOException {
Registration reg = registrationPopulator.populate();
registration.add(reg);
currentUser = reg.getUserName();
users.add(reg.getUserName());

}

private void privateComunication(PrivateChat privateChat) {
while (true) {
Message message = messagePopulator.populate();
message.setSenderName(currentUser);
System.out.println(message.getSenderName());
System.out.println("\t" + message.getContent());

if (EXIT.equals(message.getContent())) {
break;
}
privateChat.setStatus(MessageStatus.DIDNT_READ);
privateChat.addMessage(message);
}
}

private void publicComunication(Chat chat) {
while (true) {
Message message = messagePopulator.populate();
message.setSenderName(currentUser);
System.out.println(message.getSenderName());
System.out.println("\t" + message.getContent());

if (EXIT.equals(message.getContent())) {
break;
}
chat.addMessage(message);
}
}


}


Design pattern for modular application (how to reuse entities)

I have the following scenario:



  1. A JAX-RS Webservice that is responsable for the business logic and database interactions.

  2. A webapp that will be used by the end users.

  3. A webapp that will be used by administrators.


My problem is that I want to reuse the entities from the webservice on the other apps, but it is highly wrapped with frameworks like JPA, JAX-RS, CDI, among others... So I am having a hard time to isolate them. What I want is to know the best workaround and why should I use it instead of others.